def initialize(): database = lib.get_db() # Special case for new pool startup - Need 3 stats records to bootstrap block_zero = Blocks.get_by_height(0) seed_stat0 = Grin_stats(height=0, timestamp=block_zero.timestamp, gps=0, difficulty=block_zero.total_difficulty, total_utxoset_size=block_zero.num_inputs) database.db.createDataObj(seed_stat0) block_one = Blocks.get_by_height(1) seed_stat1 = Grin_stats(height=1, timestamp=block_one.timestamp, gps=0, difficulty=block_one.total_difficulty - block_zero.total_difficulty, total_utxoset_size=seed_stat0.total_utxoset_size + block_one.num_outputs - block_one.num_inputs) database.db.createDataObj(seed_stat1) block_two = Blocks.get_by_height(2) seed_stat2 = Grin_stats(height=2, timestamp=block_two.timestamp, gps=0, difficulty=block_two.total_difficulty - block_one.total_difficulty, total_utxoset_size=seed_stat1.total_utxoset_size + block_two.num_outputs - block_two.num_inputs) database.db.createDataObj(seed_stat2)
def calculate(height, avg_range=DIFFICULTY_ADJUST_WINDOW): # Get the most recent blocks from which to generate the stats recent_blocks = [] previous_stats_record = Grin_stats.get_by_height(height-1) print("XXX: {}".format(previous_stats_record)) assert previous_stats_record is not None, "No provious stats record found" recent_blocks = Blocks.get_by_height(height, avg_range) if len(recent_blocks) < min(avg_range, height): # We dont have all of these blocks in the DB raise AssertionError("Missing blocks in range: {}:{}".format(height-avg_range, height)) assert recent_blocks[-1].height == height, "Invalid height in recent_blocks[-1]" assert recent_blocks[-2].height == height - 1, "Invalid height in recent_blocks[-2]: {} vs {}".format(recent_blocks[-2].height, height - 1) # Calculate the stats data first_block = recent_blocks[0] last_block = recent_blocks[-1] timestamp = last_block.timestamp difficulty = recent_blocks[-1].total_difficulty - recent_blocks[-2].total_difficulty new_stats = Grin_stats( height = height, timestamp = timestamp, difficulty = difficulty, ) # Caclulate estimated GPS for recent edge_bits sizes all_gps = estimate_all_gps(recent_blocks) for gps in all_gps: gps_rec = Gps( edge_bits = gps[0], gps = gps[1], ) new_stats.gps.append(gps_rec) return new_stats
def initialize(avg_over_range, logger): database = lib.get_db() # Special case for new pool startup - Need 3 stats records to bootstrap block_zero = None while block_zero is None: logger.warn("Waiting for the first block record in the database") time.sleep(1) block_zero = Blocks.get_earliest() print("block_zero={}".format(block_zero)) height = block_zero.height # Create avg_over_range dummy block records prior to block_zero print("Create block filtters: {} - {}".format(height-avg_over_range, height)) for h in range(height-avg_over_range, height): print("Creating fillter at height {}".format(h)) new_block = Blocks(hash = "x", version = 0, height = h, previous = "x", timestamp = datetime.utcnow(), output_root = "x", range_proof_root = "x", kernel_root = "x", nonce = 0, edge_bits = 29, total_difficulty = block_zero.total_difficulty, secondary_scaling = 0, num_inputs = 0, num_outputs = 0, num_kernels = 0, fee = 0, lock_height = 0, total_kernel_offset = "x", state = "filler") database.db.getSession().add(new_block) database.db.getSession().commit() seed_stat0 = Grin_stats( height=height-2, timestamp=block_zero.timestamp, difficulty=block_zero.total_difficulty) database.db.createDataObj(seed_stat0) seed_stat1 = Grin_stats( height=height-1, timestamp=block_zero.timestamp, difficulty=block_zero.total_difficulty) database.db.createDataObj(seed_stat1) seed_stat2 = Grin_stats( height=height, timestamp=block_zero.timestamp, difficulty=block_zero.total_difficulty) database.db.createDataObj(seed_stat2) return height
def get(self, height=0, range=None, fields=None): database = lib.get_db() fields = lib.fields_to_list(fields) if height == 0: height = grin.get_current_height() if range == None: stat = Grin_stats.get_by_height(height) if stat is None: return None return stat.to_json(fields) else: stats = [] for stat in Grin_stats.get_by_height(height, range): stats.append(stat.to_json(fields)) return stats
def main(): CONFIG = lib.get_config() LOGGER = lib.get_logger(PROCESS) LOGGER.warn("=== Starting {}".format(PROCESS)) # Connect to DB database = lib.get_db() atexit.register(lib.teardown_db) check_interval = float(CONFIG[PROCESS]["check_interval"]) avg_over_range = int(CONFIG[PROCESS]["avg_over_range"]) # Find the height of the latest stats record last_height = 0 latest_stat = Grin_stats.get_latest() print("latest_stat = {}".format(latest_stat)) if latest_stat == None: LOGGER.warn("Initializing Grin_stats") grinstats.initialize(avg_over_range, LOGGER) latest_stat = Grin_stats.get_latest() print("Finished initializing, latest_stat height = {}".format( latest_stat.height)) last_height = latest_stat.height height = last_height + 1 LOGGER.warn( "grinStats service starting at block height: {}".format(height)) # Generate grin stats records - one per grin block while True: #latest_db_block = Blocks.get_latest() latest = Blocks.get_latest().height while latest >= height: try: new_stats = grinstats.calculate(height, avg_over_range) # Batch new stats when possible, but commit at reasonable intervals database.db.getSession().add(new_stats) # if( (height % BATCHSZ == 0) or (height >= (latest-10)) ): database.db.getSession().commit() LOGGER.warn( "Added Grin_stats for block: {} - gps:{} diff:{}".format( new_stats.height, new_stats.gps, new_stats.difficulty)) height = height + 1 except AssertionError as e: LOGGER.error("Something went wrong: {}".format(e)) sleep(check_interval) sys.stdout.flush() sleep(check_interval) LOGGER.warn("=== Completed {}".format(PROCESS))
def get(self, height=None, range=None, fields=None): LOGGER = lib.get_logger(PROCESS) LOGGER.warn("GrinAPI_stats get height:{} range:{} fields:{}".format( height, range, fields)) fields = lib.fields_to_list(fields) if height is None or height == 0: stats = Grin_stats.get_latest(range) else: stats = Grin_stats.get_by_height(height, range) #pp.pprint(stats) if range == None: if stats is None: return None return stats.to_json(fields) else: st = [] for stat in stats: st.append(stat.to_json(fields)) return st
def calculate(height, avg_range): # Get the most recent blocks from which to generate the stats recent_blocks = [] previous_stats_record = Grin_stats.get_by_height(height - 1) print("XXX: {}".format(previous_stats_record)) assert previous_stats_record is not None, "No provious stats record found" recent_blocks = Blocks.get_by_height(height, avg_range) if len(recent_blocks) < min(avg_range, height): # We dont have all of these blocks in the DB raise AssertionError("Missing blocks in range: {}:{}".format( height - avg_range, height)) print(recent_blocks[-1]) print(recent_blocks[-2]) print(recent_blocks[-3]) print(recent_blocks[-4]) assert recent_blocks[ -1].height == height, "Invalid height in recent_blocks[-1]" assert recent_blocks[ -2].height == height - 1, "Invalid height in recent_blocks[-2]: {} vs {}".format( recent_blocks[-2].height, height - 1) # Calculate the stats data first_block = recent_blocks[0] last_block = recent_blocks[-1] timestamp = last_block.timestamp difficulty = recent_blocks[-1].total_difficulty - recent_blocks[ -2].total_difficulty gps = lib.calculate_graph_rate(difficulty, first_block.timestamp, last_block.timestamp, len(recent_blocks)) # utxo set size = sum outputs - sum inputs total_utxoset_size = previous_stats_record.total_utxoset_size + last_block.num_outputs - last_block.num_inputs return Grin_stats( height=height, timestamp=timestamp, gps=gps, difficulty=difficulty, total_utxoset_size=total_utxoset_size, )
def recalculate(start_height, avg_range): database = lib.get_db() height = start_height while height <= grin.blocking_get_current_height(): old_stats = Grin_stats.get_by_height(height) new_stats = calculate(height, avg_range) if old_stats is None: database.db.createDataObj(new_stats) else: old_stats.timestamp = new_stats.timestamp old_stats.difficulty = new_stats.difficulty old_stats.gps = new_stats.gps old_stats.difficulty = new_stats.difficulty old_stats.total_utxoset_size = new_stats.total_utxoset_size database.db.getSession().commit() height = height + 1
def avg_network_gps(height=0, range=60): if height == 0: height = Blocks.get_latest().height if range <= 0: range = 1 grinstats = Grin_stats.get_by_height(height, range) gpslists = [stat.gps for stat in grinstats] gpslists_len = len(gpslists) if gpslists_len == 0: return 0 gpstotals = {} for gpslist in gpslists: for gps in gpslist: if gps.edge_bits not in gpstotals: gpstotals[gps.edge_bits] = 0 gpstotals[gps.edge_bits] += gps.gps gpsavgs = {} for sz, gpstotal in gpstotals.items(): gpsavgs[sz] = gpstotal / gpslists_len return gpsavgs
def get_stats(height): ## # Get requested grin network stats as seen by our grin node return Grin_stats.get_by_height(height)