Esempio n. 1
0
 def get(self, id=None, height=0, range=None, fields=None):
     database = lib.get_db()
     LOGGER = lib.get_logger(PROCESS)
     LOGGER.warn(
         "WorkerAPI_stats get id:{} height:{} range:{} fields:{}".format(
             id, height, range, fields))
     fields = lib.fields_to_list(fields)
     if height == 0:
         height = Blocks.get_latest().height
     stats = []
     if id is None:
         for stat in Worker_stats.get_by_height(height, range):
             #print("YYY: {}".format(stats))
             stats.append(stat.to_json(fields))
         return stats
     else:
         if range is None:
             res = Worker_stats.get_by_height_and_id(id, height)
             if res is None:
                 return "[]".to_json()
             return res.to_json(fields)
         else:
             for stat in Worker_stats.get_by_height_and_id(
                     id, height, range):
                 stats.append(stat.to_json(fields))
             return stats
Esempio n. 2
0
def main():
    global LOGGER
    global CONFIG

    CONFIG = lib.get_config()
    LOGGER = lib.get_logger(PROCESS)
    database = lib.get_db()
    LOGGER.warn("=== Starting {}".format(PROCESS))

    check_interval = float(CONFIG[PROCESS]["check_interval"])
    max_rebuild_depth = float(CONFIG[PROCESS]["max_rebuild_depth"])
    avg_over_range_grin = int(CONFIG["grinStats"]["avg_over_range"])
    avg_over_range_pool = int(CONFIG["poolStats"]["avg_over_range"])
    avg_over_range_worker = int(CONFIG["workerStats"]["avg_over_range"])

    current_height = grin.blocking_get_current_height()
    rebuild_height = current_height - max_rebuild_depth

    while True:
        # Grin blocks and therefore grin stats cant be dirty
        #        # Check for dirty grin stats
        #        dirty = Grin_stats.get_first_dirty()
        #        if dirty is not None:
        #            LOGGER.warn("Recalculating Grin Stats from {}".format(dirty.height))
        #            end_height = grinstats.recalculate(dirty.height, avg_over_range_grin)
        #            LOGGER.warn("Finished Recalculating Grin Stats: {} - {}".format(dirty.height, end_height))
        # Check for dirty pool stats
        dirty = Pool_stats.get_first_dirty(rebuild_height)
        if dirty is not None:
            LOGGER.warn("Recalculating Pool Stats from {}".format(
                dirty.height))
            end_height = poolstats.recalculate(dirty.height,
                                               avg_over_range_pool)
            LOGGER.warn("Finished Recalculating Pool Stats: {} - {}".format(
                dirty.height, end_height))


#        # Check for dirty worker stats
        dirty = Worker_stats.get_first_dirty(rebuild_height)
        while dirty is not None:
            LOGGER.warn("Recalculating Worker Stats for {} from {}".format(
                dirty.height, avg_over_range_worker))
            end_height = workerstats.recalculate(dirty.height,
                                                 avg_over_range_worker)
            LOGGER.warn(
                "Finished Recalculating Worker Stats for {} - {}".format(
                    dirty.height, end_height))
            dirty = Worker_stats.get_first_dirty()

        sys.stdout.flush()
        time.sleep(check_interval)

    LOGGER.warn("=== Completed {}".format(PROCESS))
Esempio n. 3
0
def main():
    CONFIG = lib.get_config()
    LOGGER = lib.get_logger(PROCESS)
    LOGGER.warn("=== Starting {}".format(PROCESS))
    # Connect to DB
    database = lib.get_db()

    # Get config
    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 = Worker_stats.get_latest()

    if latest_stat != None:
        last_height = latest_stat.height
    height = last_height + 1
    LOGGER.warn("Starting at block height: {}".format(height))

    # Generate worker stats records - one per grin block for each active worker
    while True:
        # latest = grin.blocking_get_current_height()
        latest = Blocks.get_latest().height
        #LOGGER.warn("Latest Network Block Height = {}".format(latest))
        while latest > height:
            try:
                new_stats = workerstats.calculate(height, avg_over_range)
                LOGGER.warn("{} new stats for height {}".format(
                    len(new_stats), height))
                # mark any existing pool_stats dirty
                pool_stats = Pool_stats.get_by_height(height)
                if pool_stats is not None:
                    LOGGER.warn(
                        "Marked existing pool_stats dirty for height: {}".
                        format(height))
                    pool_stats.dirty = True
                database.db.getSession().bulk_save_objects(new_stats)
                if ((height % BATCHSZ == 0) or (height >= (latest - 10))):
                    database.db.getSession().commit()
                for stats in new_stats:
                    LOGGER.warn(
                        "Added Worker_stats for block: {}, Worker: {} - {} {} {} {} {} {}"
                        .format(stats.height, stats.worker, stats.gps,
                                stats.shares_processed,
                                stats.total_shares_processed, stats.grin_paid,
                                stats.total_grin_paid, stats.balance))
                height = height + 1
            except Exception as e:
                LOGGER.error("Something went wrong: {}".format(e))
                LOGGER.error("Traceback: {}".format(
                    traceback.format_exc().splitlines()))
                database.db.getSession().rollback()
                sleep(check_interval)
        sys.stdout.flush()
        sleep(check_interval)
    LOGGER.warn("=== Completed {}".format(PROCESS))
Esempio n. 4
0
def calculate(height, avg_range):
    avg_over_first_grin_block = Blocks.get_by_height( max(height-avg_range, 1) )
    assert avg_over_first_grin_block is not None, "Missing grin block: {}".format(max(height-avg_range, 1))
    grin_block = Blocks.get_by_height(height)
    assert grin_block is not None, "Missing grin block: {}".format(height)
    # Get all workers share records for the current range of blocks
    latest_worker_shares = Worker_shares.get_by_height(height)
#    assert len(latest_worker_shares) != 0, "Missing worker shares record for height {}".format(height)
    avg_over_worker_shares = Worker_shares.get_by_height(height, avg_range)
    # Create a worker_stats for each user who submitted a share in this range
    workers = list(set([share.worker for share in latest_worker_shares]))
    new_stats = []
    for worker in workers:
        # Get this workers most recent worker_stats record (for running totals)
        last_stat = Worker_stats.get_latest_by_id(worker)
        if last_stat is None:
            # A new worker
            last_stat = Worker_stats(None, datetime.utcnow(), height-1, worker, 0, 0, 0, 0, 0, 0)
            new_stats.append(last_stat)
        # Calculate this workers stats data
        timestamp = grin_block.timestamp
        difficulty = POOL_MIN_DIFF # XXX TODO - enchance to support multiple difficulties
        num_shares_in_range = sum([shares.valid for shares in avg_over_worker_shares if shares.worker == worker])
        gps = grin.calculate_graph_rate(difficulty, avg_over_first_grin_block.timestamp, grin_block.timestamp, num_shares_in_range)
        num_valid_this_block = [shares.valid for shares in latest_worker_shares if shares.worker == worker][0]
        num_invalid_this_block = [shares.invalid for shares in latest_worker_shares if shares.worker == worker][0]
        shares_processed = num_valid_this_block + num_invalid_this_block
#        latest_worker_shares = [share for share in latest_pool_shares if share.found_by == worker]
        #shares_processed = len(worker_shares_this_block)
        total_shares_processed = last_stat.total_shares_processed + shares_processed
        stats = Worker_stats(
                id = None,
                height = height,
                timestamp = timestamp,
                worker = worker,
                gps = gps,
                shares_processed = shares_processed,
                total_shares_processed = total_shares_processed,
                grin_paid = 123, # XXX TODO
                total_grin_paid = 456, # XXX TODO
                balance = 1) # XXX TODO
        new_stats.append(stats)
    return new_stats
Esempio n. 5
0
 def get(self, id=None, height=0, range=None, fields=None):
     database = lib.get_db()
     fields = lib.fields_to_list(fields)
     if height == 0:
         height = grin.get_current_height()
     stats = []
     if id == None:
         for stat in Worker_stats.get_by_height(height, range):
             stats.append(stat.to_json(fields))
         return stats
     else:
         if range == None:
             res = Worker_stats.get_by_height_and_id(id, height)
             if res is None:
                 return res
             return res.to_json(fields)
         else:
             for stat in Worker_stats.get_by_height_and_id(
                     id, height, range):
                 stats.append(stat.to_json(fields))
             return stats
Esempio n. 6
0
 def get(self, id, height=0, range=None, fields=None):
     global database
     #database = lib.get_db()
     LOGGER = lib.get_logger(PROCESS)
     # AUTH FILTER
     if id != g.user.id:
         response = jsonify(
             {'message': 'Not authorized to access data for other users'})
         response.status_code = 403
         return response
     debug and LOGGER.warn(
         "WorkerAPI_stats get id:{} height:{} range:{} fields:{}".format(
             id, height, range, fields))
     # Enforce range limit
     if range is not None:
         range = min(range, worker_stats_range_limit)
     fields = lib.fields_to_list(fields)
     res = None
     if range is None:
         # Getting a single record
         if height == 0:
             # Get the most recent stats for this user
             res = Worker_stats.get_latest_by_id(id)
         else:
             res = Worker_stats.get_by_height_and_id(id, height)
         if res is None:
             return None
         return res.to_json(fields)
     else:
         # Getting a range of records
         if height == 0:
             height = Blocks.get_latest().height
         res = Worker_stats.get_by_height_and_id(id, height, range)
         if res is None:
             return None
         stats = []
         for stat in res:
             stats.append(stat.to_json(fields))
         return stats
Esempio n. 7
0
    def get(self, height=0, range=None, fields=None):
        global database
        #database = lib.get_db()
        LOGGER = lib.get_logger(PROCESS)
        debug and LOGGER.warn("WorkersAPI_stats get height:{} range:{} fields:{}".format(height, range, fields))
        fields = lib.fields_to_list(fields)
        stats = []
        if height == 0:
            height = Blocks.get_latest().height
        for stat in Worker_stats.get_by_height(height, range):
            # AUTH FILTER
            if stat.user_id == ADMIN_ID:
                stats.append(stat.to_json(fields))

        return stats
Esempio n. 8
0
def recalculate(start_height, avg_range):
    database = lib.get_db()
    height = start_height
    while height <= grin.blocking_get_current_height():
        old_stats = Worker_stats.get_by_height(height)
        new_stats = calculate(height, avg_range)
        for old_stat in old_stats:
            database.db.deleteDataObj(old_stat)
        for stats in new_stats:
            print("new/updated stats: {} ".format(stats))
            database.db.getSession().add(stats)
            if(height % BATCHSZ == 0):
                database.db.getSession().commit()
        height = height + 1
    database.db.getSession().commit()
Esempio n. 9
0
 def post(self):
     global database
     LOGGER = lib.get_logger(PROCESS)
     username = None
     password = None
     try:
         debug and print("json request = {}".format(request.form))
         username = request.form.get('username')
         password = request.form.get('password')
         debug and LOGGER.warn("PoolAPI_users POST: user:{} password:{}".format(username, password))
     except AttributeError as e:
         LOGGER.warn("Missing username or password - {}".format(str(e)))
     if username is None or password is None:
         response = jsonify({ 'message': 'Missing arguments: username and pasword required' })
         response.status_code = 400
         return response
     if username == "" or password == "":
         response = jsonify({ 'message': 'Missing arguments: username and pasword required' })
         response.status_code = 400
         return response
     if "." in username:
         response = jsonify({ 'message': 'Invalid Username: May not contain "."' })
         response.status_code = 400
         return response
     # Check if the username is taken
     exists = Users.check_username_exists(username)
     if exists:
         debug and print("Failed to add - conflict with existing user = {}".format(username))
         response = jsonify({ 'message': 'Conflict with existing account' })
         response.status_code = 409
         return response
     # Create the users record
     user_rec = Users.create(username, password)
     if user_rec is None:
         debug and print("Failed to add - unable to create a new user record")
         response = jsonify({ 'message': 'System Error: Failed to create account' })
         response.status_code = 500
         return response
     # initialize a worker_stats record for this user (previous block) so they get instance feedback on the UI
     lb = Blocks.get_latest()
     if lb is not None:
         height = Blocks.get_latest().height
         initial_stat = Worker_stats(datetime.utcnow(), height, user_rec.id)
         database.db.createDataObj(initial_stat)
     debug and print("Added user = {}".format(user_rec))
     response = jsonify({ 'username': user_rec.username, 'id': user_rec.id })
     response.status_code = 201
     return response
Esempio n. 10
0
def main():
    global LOGGER
    LOGGER = lib.get_logger(PROCESS)
    LOGGER.warn("=== Starting {}".format(PROCESS))

    # Connect to DB
    database = lib.get_db()

    latest_block = 0

    # XXX All in one db transaction....
    # Get unlocked blocks from the db
    unlocked_blocks = Pool_blocks.get_all_unlocked()
    database.db.getSession().commit()
    for pb in unlocked_blocks:
        try:
            LOGGER.warn("Processing unlocked block: {}".format(pb))
            if pb.height > latest_block:
                latest_block = pb.height
            # Get Worker_stats of this block to calculate reward for each worker
            worker_stats = Worker_stats.get_by_height(pb.height)
            # Calculate Payment info:
            if len(worker_stats) > 0:
                # Calcualte reward/share:
                # XXX TODO: Enhance
                #  What algorithm to use?  Maybe: https://slushpool.com/help/manual/rewards
                r_per_g = REWARD / sum([st.gps for st in worker_stats])
                for stat in worker_stats:
                    # Calculate reward
                    worker_rewards = stat.gps * r_per_g
                    # Add or create worker rewards
                    worker_utxo = Pool_utxo.credit_worker(
                        stat.worker, worker_rewards)
                    LOGGER.warn("Credit to user: {} = {}".format(
                        stat.worker, worker_rewards))
            # Mark the pool_block state="paid" (maybe "processed" would be more accurate?)
            pb.state = "paid"
            database.db.getSession().commit()
        except Exception as e:
            database.db.getSession().rollback()
            LOGGER.error("Something went wrong: {}".format(e))

    #database.db.getSession().commit()
    LOGGER.warn("=== Completed {}".format(PROCESS))
    sys.stdout.flush()
Esempio n. 11
0
def recalculate(start_height, avg_range):
    database = lib.get_db()
    height = start_height
    while height <= grin.blocking_get_current_height():
        old_stats = Worker_stats.get_by_height(height)
        new_stats = calculate(height, avg_range)
        for old_stat in old_stats:
            database.db.deleteDataObj(old_stat)
        for stats in new_stats:
            print("new/updated stats: {} ".format(stats))
            worker = stats.worker
            database.db.getSession().add(stats)
            if (height % BATCHSZ == 0):
                database.db.getSession().commit()
        height = height + 1
    # We updated one or more worker stats so we mark the Pool_stats dirty
    stats_rec = Pool_stats.get_by_height(height)
    if stats_rec is not None:
        stats_rec.dirty = True
    database.db.getSession().commit()
Esempio n. 12
0
def main():
    CONFIG = lib.get_config()
    LOGGER = lib.get_logger(PROCESS)
    LOGGER.warn("=== Starting {}".format(PROCESS))
    # Connect to DB
    database = lib.get_db()

    # Get config
    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 = Worker_stats.get_latest()

    if latest_stat != None:
        last_height = latest_stat.height
    else:
        latest = Blocks.get_latest()
        while latest is None:
            LOGGER.warn("Waiting for the first block...")
            sleep(10)
            latest = Blocks.get_latest()
        last_height = latest.height
    height = last_height + 1

    LOGGER.warn("Starting at block height: {}".format(height))

    # Generate worker stats records - one per grin block for each active worker
    while True:
        # latest = grin.blocking_get_current_height()
        latest = Blocks.get_latest().height
        share_height = Worker_shares.get_latest_height()
        while share_height is None:
            LOGGER.warn("waiting for the first worker shares")
            sleep(10)
            share_height = Worker_shares.get_latest_height()
        stats_height = height - 1
        LOGGER.warn(
            "Running: chain height: {}, share height: {} vs stats height: {}".
            format(latest, share_height, stats_height))
        while share_height > height:
            try:
                new_stats = workerstats.calculate(height, avg_over_range)
                LOGGER.warn("{} new stats for height {}".format(
                    len(new_stats), height))
                for stats in new_stats:
                    LOGGER.warn("Added Worker_stats: {}".format(stats))
                # mark any existing pool_stats dirty
                pool_stats = Pool_stats.get_by_height(height)
                for stat_rec in new_stats:
                    database.db.getSession().add(stat_rec)
                if pool_stats is not None:
                    LOGGER.warn(
                        "Marked existing pool_stats dirty for height: {}".
                        format(height))
                    pool_stats.dirty = True  # Pool_stats need to be recalculated
                if ((height % BATCHSZ == 0) or (height >= (latest - 10))):
                    LOGGER.warn("Commit ---")
                    database.db.getSession().commit()
                height = height + 1
            except Exception as e:
                LOGGER.exception("Something went wrong: {}".format(e))
                database.db.getSession().rollback()
                sleep(check_interval)
        sys.stdout.flush()
        sleep(check_interval)
    LOGGER.warn("=== Completed {}".format(PROCESS))
Esempio n. 13
0
def calculate(height, window_size):
    database = lib.get_db()
    grin_block = Blocks.get_by_height(height)
    assert grin_block is not None, "Missing grin block at height: {}".format(height)
    # Get all Worker_share records in the estimation window
    window = Worker_shares.get_by_height(height, window_size)
    # Get list of all workers who submitted shares OR recvd a payment at this height
    pmts = Pool_payment.get_by_height(height)
    shares_workers = [share.user_id for share in window]
    pmts_workers = [pmt.user_id for pmt in pmts]
    workers = list(set(shares_workers + pmts_workers))
    # Create a new Worker_stats record for each of these workers
    print("Calcualte worker stats for height {}, workers {}".format(height, workers))
    new_stats = []
    for worker in workers:
        # Get this workers most recent worker_stats record (for running totals)
        last_stat = Worker_stats.get_latest_by_id(worker)
        if last_stat is None:
            # A new worker, initialize a last_stat for the previous block
            last_stat = Worker_stats(
                            timestamp=datetime.utcnow(),
                            height=height-1,
                            user_id=worker)
            new_stats.append(last_stat)
        # Calculate this workers stats data
        timestamp = grin_block.timestamp
        # Caclulate estimated GPS for all sizes with shares submitted
        all_gps = estimate_gps_for_all_sizes(worker, window)
        # Keep track of share totals - sum counts of all share sizes submitted for this block
        print("Looking up shares for height {} user {}".format(height, worker))
        this_workers_shares_this_block = Worker_shares.get_by_height_and_id(height, worker)
        print("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX {}".format(this_workers_shares_this_block))
        if this_workers_shares_this_block is None or len(this_workers_shares_this_block) == 0:
            this_workers_valid_shares = 0
            this_workers_invalid_shares = 0
            this_workers_stale_shares = 0
        else:
            this_workers_shares_this_block = this_workers_shares_this_block[-1]
            this_workers_valid_shares = this_workers_shares_this_block.num_valid()
            this_workers_invalid_shares = this_workers_shares_this_block.num_invalid()
            this_workers_stale_shares = this_workers_shares_this_block.num_stale()
        this_workers_total_valid_shares = last_stat.total_valid_shares + this_workers_valid_shares
        this_workers_total_invalid_shares = last_stat.total_invalid_shares + this_workers_invalid_shares
        this_workers_total_stale_shares = last_stat.total_stale_shares + this_workers_stale_shares

        # XXX PERFORAMCE = removed bulk insert to debug some other issue, need to put bulk insert back!!!
        stats = Worker_stats(
                height = height,
                timestamp = timestamp,
                user_id = worker,
                valid_shares = this_workers_valid_shares,
                invalid_shares = this_workers_invalid_shares,
                stale_shares = this_workers_stale_shares,
                total_valid_shares = this_workers_total_valid_shares,
                total_invalid_shares = this_workers_total_invalid_shares,
                total_stale_shares = this_workers_total_stale_shares,
            )
        database.db.getSession().add(stats)
        database.db.getSession().commit()
        #print("AAA: Created Worker_stats with id={}".format(stats.id))
#        print("all_gps for worker {}:".format(worker))
#        pp.pprint(all_gps)
        for gps_est in all_gps:
            gps_rec = Gps(
                edge_bits = gps_est[0],
                gps = gps_est[1],
            )
            stats.gps.append(gps_rec)
            #print("AAA: Appended gps record to Worker_stats: {}".format(gps_rec))
#            gps_rec.worker_stats_id = stats.id,
#            database.db.getSession().add(gps_rec)
        new_stats.append(stats)
        database.db.getSession().add(stats)
        database.db.getSession().commit()
    sys.stdout.flush()
    return new_stats
Esempio n. 14
0
def calculate(height, window_size):
    database = lib.get_db()
    grin_block = Blocks.get_by_height(height)
    assert grin_block is not None, "Missing grin block at height: {}".format(
        height)
    # Get all Worker_share records in the estimation window
    window = Worker_shares.get_by_height(height, window_size)
    # Get list of all workers who submitted shares in the window
    workers = list(set([share.worker for share in window]))
    # Create a new Worker_stats record for each of these workers
    print("Calcualte worker stats for height {}, workers {}".format(
        height, workers))
    new_stats = []
    for worker in workers:
        # Get this workers most recent worker_stats record (for running totals)
        last_stat = Worker_stats.get_latest_by_id(worker)
        if last_stat is None:
            # A new worker, initialize a last_stat for the previous block
            last_stat = Worker_stats(None, datetime.utcnow(), height - 1,
                                     worker, 0, 0, 0, 0, 0, 0)
            new_stats.append(last_stat)
        # Calculate this workers stats data
        timestamp = grin_block.timestamp
        # Caclulate estimated GPS for all sizes with shares submitted
        all_gps = estimate_gps_for_all_sizes(worker, window)
        # Keep track of share totals - sum counts of all share sizes submitted for this block
        this_workers_shares = [ws for ws in window if ws.worker == worker]
        num_shares_processed = this_workers_shares[-1].num_shares()
        print("num_shares_processed={}".format(num_shares_processed))
        total_shares_processed = last_stat.total_shares_processed + num_shares_processed
        print("total_shares_processed={}".format(total_shares_processed))

        # XXX PERFORAMCE = could not get bulk_insert to work...

        stats = Worker_stats(
            id=None,
            height=height,
            timestamp=timestamp,
            worker=worker,
            shares_processed=num_shares_processed,
            total_shares_processed=total_shares_processed,
            grin_paid=123,  # XXX TODO
            total_grin_paid=456,  # XXX TODO
            balance=1)  # XXX TODO
        database.db.getSession().add(stats)
        database.db.getSession().commit()
        #print("AAA: Created Worker_stats with id={}".format(stats.id))
        #        print("all_gps for worker {}:".format(worker))
        #        pp.pprint(all_gps)
        for gps_est in all_gps:
            gps_rec = Gps(
                edge_bits=gps_est[0],
                gps=gps_est[1],
            )
            stats.gps.append(gps_rec)
            #print("AAA: Appended gps record to Worker_stats: {}".format(gps_rec))
#            gps_rec.worker_stats_id = stats.id,
#            database.db.getSession().add(gps_rec)
#        new_stats.append(stats)
        database.db.getSession().add(stats)
        database.db.getSession().commit()
    sys.stdout.flush()
    return new_stats