js代码改写py
This commit is contained in:
265
m2pool_backend_app/src/distribution.py
Normal file
265
m2pool_backend_app/src/distribution.py
Normal file
@@ -0,0 +1,265 @@
|
||||
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']
|
||||
Reference in New Issue
Block a user