import asyncio from datetime import datetime from decimal import Decimal, ROUND_DOWN from public.times import Times from init import Init class Distribution(Init): def __init__(self, coin): method = "distribution" super().__init__(coin, method) # ------------------ 核心方法 ------------------ def score(self, alluser_mhs24h, hash_percent=1): total_hashrate = sum(obj['mhs24h'] for obj in alluser_mhs24h) result = {} for item in alluser_mhs24h: user = item['user'] mhs24h = item['mhs24h'] result[user] = round((mhs24h / total_hashrate) * hash_percent, 4) return result async def query_last_day_hashrate(self, date): try: sql = f"SELECT user, SUM(mhs24h) AS mhs24h FROM {self.coin}_mhsv2 WHERE date = ? GROUP BY user;" data = await self.hashratedb.exec(sql, [date + " 00:00:00"]) if not data: return False return [{'user': item['user'], 'mhs24h': float(item['mhs24h'])} for item in data] except Exception as err: raise err async def query_last_day_reward(self, start_time, end_time): try: sql = f""" SELECT MAX(height) AS max_height, SUM(reward) AS reward FROM {self.coin}_blkreportprofitv2 WHERE date >= ? AND date < ? AND state = ?; """ data = await self.distributiondb.exec(sql, [start_time, end_time, 1]) if not data or not data[0]['reward']: return 0 # 返回整数,完全对齐 JS BigInt data[0]['reward'] = int(data[0]['reward']) return data except Exception as err: raise err async def query_last_day_if_mature(self, start_time, end_time): try: sql = f"SELECT count(*) AS count FROM {self.coin}_blkreportprofitv2 WHERE date >= ? AND date < ? AND state = ?;" while True: await asyncio.sleep(60 * 15) data = await self.distributiondb.exec(sql, [start_time, end_time, 0]) current_hour = int(Times.times()[4]) if (self.coin == "rxd" and current_hour >= 9) or (self.coin != "rxd" and current_hour >= 4): return False if data[0]['count'] == 0: break return True except Exception as err: raise err async def query_users_address(self): try: sql = """ SELECT a.miner_user AS 'user', b.balance AS 'address', b.amount AS 'amount', b.active AS 'state', b.min_amount AS 'min_amount' FROM user_account_balance b LEFT JOIN user_miner_account a ON b.ma_id = a.id WHERE a.coin = ? AND b.status = 0; """ data = await self.users_addresses.exec(sql, [self.coin]) return data if data else False except Exception as err: raise err async def verify_block(self, height): try: data = await self.node.verify_block(height, self.REPORT_ADDRESS) if data and isinstance(data, dict): return await self.node.block(data) return False except Exception as err: raise err async def insert_blkreportprofit(self, data): try: sql = f"INSERT INTO {self.coin}_blkreportprofitv2 (date, height, hash, reward, fees, state) VALUES " values = [] for item in data: sql += "(?,?,?,?,?,?), " values.extend([ Times.utcTime(item['time'] * 1000), item['height'], item['hash'], item['block_reward'], item['block_fees'], 1 ]) sql = sql[:-2] await self.distributiondb.exec_transaction(sql, values) except Exception as err: raise err async def check_last_data_blk(self, date): try: ts = int(datetime.fromisoformat(date).timestamp() * 1000) - 86400000 yMd = Times.utcTime(ts).split(" ")[0] ymd = yMd.split("-") table_name = f"{self.coin}_pool_blkstats_{ymd[0]}{ymd[1]}{ymd[2]}" confirm_result = await self.pooldb.exec(f"SHOW TABLES LIKE '{table_name}';") if not confirm_result: print("pool_blkstats表未更新,退出本次执行,请手动校验") return False pool_data = await self.pooldb.exec(f"SELECT height FROM {table_name} WHERE DATE(date) >= ?;", [yMd]) heights = [item['height'] for item in pool_data] blkreport_data = await self.distributiondb.exec(f"SELECT height FROM {self.coin}_blkreportprofitv2 WHERE DATE(date)=? AND state=?;", [yMd, 1]) blkreport_heights = [item['height'] for item in blkreport_data] need_check_heights = [h for h in heights if h not in set(blkreport_heights)] if not need_check_heights: print(f"{self.coin}check 完成,没有需要重新校验的区块") return True need_insert_data = [] for height in need_check_heights: result = await self.verify_block(height) if result: need_insert_data.append(result) if need_insert_data: await self.insert_blkreportprofit(need_insert_data) print(f"{self.coin}check 完成,已将{self.coin}漏掉的报块全部插入blk表中!") else: print(f"{self.coin}check 完成,没有需要insert的区块") return True except Exception as err: raise err async def update_state(self, min_amount): try: data = await self.distributiondb.exec("SELECT user, SUM(amount) AS profit FROM wallet_in WHERE coin=? AND state=? GROUP BY user;", [self.coin, 2]) if not data: return for item in data: user = item['user'] if item['profit'] >= min_amount[user]: await self.distributiondb.exec("UPDATE wallet_in SET state=? WHERE coin=? AND user=?", [0, self.coin, user]) except Exception as err: raise err async def insert_wallet_in(self, data): try: sql = "INSERT INTO wallet_in(coin, user, address, create_date, should_out_date, max_height, amount, state) VALUES " values = [] for item in data: sql += "(?,?,?,?,?,?,?,?), " values.extend([ item['coin'], item['user'], item['address'], item['create_date'], item['should_out_date'], item['max_height'], item['amount'], item['state'] ]) sql = sql[:-2] await self.distributiondb.exec_transaction(sql, values) except Exception as err: raise err async def main(self, start_time, end_time): try: if not await self.query_last_day_if_mature(start_time, end_time): return if not await self.check_last_data_blk(end_time): return last_day_mhs24h, last_day_reward, users_address = await asyncio.gather( self.query_last_day_hashrate(end_time), self.query_last_day_reward(start_time, end_time), self.query_users_address() ) if not last_day_mhs24h or not last_day_reward or not users_address: print("查询错误", last_day_mhs24h, last_day_reward, users_address) return reward = (int(last_day_reward[0]['reward']) * int((1 - self.POOL_FEE) * 10000)) // 10000 score_ratio = self.score(last_day_mhs24h, 1) max_height = last_day_reward[0]['max_height'] should_out_date = end_time accuracy = 100000000 count = 8 if self.coin == "nexa": should_out_date = Times.utcTime(int(datetime.fromisoformat(end_time).timestamp() * 1000) + 1000 * 60 * 60 * 24 * 7) accuracy = 100 count = 2 elif self.coin == "rxd": accuracy = 100 count = 2 elif self.coin == "alph": accuracy = 0 count = 0 user_profit = 0 result = [] pool_account_address = None min_amount = {} for user, ratio in score_ratio.items(): ratio_int = round(ratio * 10000) profit = reward * ratio_int // 10000 if profit == 0: continue user_profit += profit for item in users_address: if item['user'] == "pool_account": pool_account_address = item['address'] if item['user'] == user: min_amount[user] = item['min_amount'] state = 0 if profit >= item['amount'] and item['state'] == 0: state = 0 elif profit < item['amount'] and item['state'] == 0: state = 2 elif profit >= item['amount'] and item['state'] == 1: state = 3 else: state = 4 result.append({ 'coin': self.coin, 'user': user, 'address': item['address'], 'create_date': end_time, 'should_out_date': should_out_date, 'max_height': max_height, 'amount': profit, 'state': state }) pool_account_amount = Decimal(last_day_reward[0]['reward']) - Decimal(user_profit) pool_account_amount = pool_account_amount.quantize(Decimal(f'1e-{count}'), rounding=ROUND_DOWN) result.append({ 'coin': self.coin, 'user': "pool_account", 'address': pool_account_address, 'create_date': end_time, 'should_out_date': should_out_date, 'max_height': max_height, 'amount': float(pool_account_amount), 'state': 0 }) print(result) await self.insert_wallet_in(result) await self.update_state(min_amount) except Exception as err: raise err __all__ = ['Distribution']