This commit is contained in:
lzx 2025-04-16 19:01:49 +08:00
parent 90ae1d1852
commit 7e63afd621
6 changed files with 125 additions and 204 deletions

View File

@ -1,23 +1,13 @@
m2pool app module code
environment:
nodejs v16.10.0 +
express
axios
mysql2
ioredis
if you want to support enx, kas...
add environment:
kaspa-rpc-client
### start common example ### ### start common example ###
node app.js <method> <coin> node app.js <method> <coin>
# if you used pm2, you can use this command # if you used pm2, you can use this command
```
pm2 start app.js --name <pm2 process name> -- <method> <coin> pm2 start app.js --name <pm2 process name> -- <method> <coin>
```
# config like this # config like this
```
// this file please use .conf // this file please use .conf
{ {
"master":{ "master":{
@ -148,9 +138,9 @@ pm2 start app.js --name <pm2 process name> -- <method> <coin>
"POOL_FEE": "POOL_FEE":
} }
} }
```
# start examples
############# pm2 start common ############# ############# pm2 start common #############
pm2 start app.js --name nexa-hashratev2 -- hashrate nexa pm2 start app.js --name nexa-hashratev2 -- hashrate nexa
pm2 start app.js --name nexa-reportv2 -- report nexa pm2 start app.js --name nexa-reportv2 -- report nexa
@ -175,13 +165,11 @@ pm2 start app.js --name dgbs-hashratev2 -- hashrate dgbs
pm2 start app.js --name dgbs-reportv2 -- report dgbs pm2 start app.js --name dgbs-reportv2 -- report dgbs
pm2 start app.js --name dgbs-confirm -- confirm dgbs pm2 start app.js --name dgbs-confirm -- confirm dgbs
pm2 start app.js --name dgbs-distributionv2 -- distribution dgbs pm2 start app.js --name dgbs-distributionv2 -- distribution dgbs
# pm2 start app.js --name dgbs-balance -- balance dgbs
pm2 start app.js --name dgbq-hashratev2 -- hashrate dgbq pm2 start app.js --name dgbq-hashratev2 -- hashrate dgbq
pm2 start app.js --name dgbq-reportv2 -- report dgbq pm2 start app.js --name dgbq-reportv2 -- report dgbq
pm2 start app.js --name dgbq-confirm -- confirm dgbq pm2 start app.js --name dgbq-confirm -- confirm dgbq
pm2 start app.js --name dgbq-distributionv2 -- distribution dgbq pm2 start app.js --name dgbq-distributionv2 -- distribution dgbq
# pm2 start app.js --name dgbq-balance -- balance dgbq
pm2 start app.js --name dgbo-hashratev2 -- hashrate dgbo pm2 start app.js --name dgbo-hashratev2 -- hashrate dgbo
pm2 start app.js --name dgbo-reportv2 -- report dgbo pm2 start app.js --name dgbo-reportv2 -- report dgbo

78
app.js
View File

@ -1,12 +1,10 @@
const schedule = require("node-schedule"); const schedule = require("node-schedule");
// const executeWithRetry = require("./public/retry")
const Times = require("./public/times"); const Times = require("./public/times");
const HashRate = require("./src/hashrate"); const HashRate = require("./src/hashrate");
const { Report, ReportEnx } = require("./src/report"); const { Report, ReportEnx } = require("./src/report");
const Confirm = require("./src/confirm"); const Confirm = require("./src/confirm");
const Distribution = require("./src/distribution"); const Distribution = require("./src/distribution");
const { Balance, DGBBlance } = require("./src/blanace"); const { Balance, DGBBlance } = require("./src/blanace");
const Stats = require("./src/stat");
const ClearDBData = require("./src/clear"); const ClearDBData = require("./src/clear");
const Notice = require("./src/notice"); const Notice = require("./src/notice");
@ -62,45 +60,59 @@ if (method === "report") {
if (method === "confirm") { if (method === "confirm") {
let interval = 60000;
if (coin === "rxd") {
interval = 300000;
} else if (coin === "nexa") {
interval = 120000;
}
const confirm = new Confirm(coin); const confirm = new Confirm(coin);
schedule.scheduleJob({ hour: [1], minute: [30], second: [0] }, async () => { setInterval(() => {
confirm.main(); confirm.main();
}); }, interval);
} }
// if (method === "confirm") {
// const confirm = new Confirm(coin);
// schedule.scheduleJob({ hour: [1], minute: [30], second: [0] }, async () => {
// confirm.main();
// });
// }
if (method === "distribution") { if (method === "distribution") {
const distribution = new Distribution(coin); const distribution = new Distribution(coin);
schedule.scheduleJob({ hour: [0], minute: [1], second: [0] }, async () => { // schedule.scheduleJob({ hour: [0], minute: [1], second: [0] }, async () => {
const now_ts = Date.now().valueOf(); const now_ts = Date.now().valueOf();
const last_ts = now_ts - 1000 * 60 * 60 * 24; const last_ts = now_ts - 1000 * 60 * 60 * 24;
const ymd_now = Times.utcTime(now_ts); const ymd_now = Times.utcTime(now_ts);
const ymd_last = Times.utcTime(last_ts); const ymd_last = Times.utcTime(last_ts);
const end_time = ymd_now.split(" ")[0] + " 00:00:00"; const end_time = ymd_now.split(" ")[0] + " 00:00:00";
const start_time = ymd_last.split(" ")[0] + " 00:00:00"; const start_time = ymd_last.split(" ")[0] + " 00:00:00";
await distribution.main(start_time, end_time); distribution.main(start_time, end_time);
}); // });
} }
if (method === "balance") { // if (method === "balance") {
const special_coins = ["dgbo", "dgbs", "dgbq"]; // const special_coins = ["dgbo", "dgbs", "dgbq"];
let balance; // let balance;
if (special_coins.includes(coin)) { // if (special_coins.includes(coin)) {
balance = new DGBBlance(coin); // balance = new DGBBlance(coin);
} else { // } else {
balance = new Balance(coin); // balance = new Balance(coin);
} // }
balance // balance
.main() // .main()
.then(() => { // .then(() => {
console.log("ok"); // console.log("ok");
}) // })
.catch((err) => { // .catch((err) => {
console.log(err); // console.log(err);
}) // })
.finally(() => { // .finally(() => {
process.exit(0); // process.exit(0);
}); // });
} // }
if (method === "balance") { if (method === "balance") {
const special_coins = ["dgbo", "dgbs", "dgbq"]; const special_coins = ["dgbo", "dgbs", "dgbq"];
@ -123,9 +135,11 @@ if (method === "balance") {
// 最多执行 36 次 (6小时) // 最多执行 36 次 (6小时)
const enable = await balance.query_now_height(last_height); const enable = await balance.query_now_height(last_height);
if (enable) { if (enable) {
await balance.main(); const result = await balance.main();
console.log(`${coin}转账已完成`); if(!result){
return; // 成功执行后退出循环 console.log(`${coin}转账已完成`);
return; // 成功执行后退出循环
}
} }
console.log(`等待中... (已等待 ${count * 10} 分钟)`); console.log(`等待中... (已等待 ${count * 10} 分钟)`);
await balance.sleep(1000 * 60 * 10); // 休眠 10 分钟 await balance.sleep(1000 * 60 * 10); // 休眠 10 分钟
@ -138,10 +152,6 @@ if (method === "balance") {
}); });
} }
if (method === "stats") {
const stats = new Stats(coin);
stats.caculate_user_should_distribution("2024-11-28 00:00:00");
}
if (method === "clear") { if (method === "clear") {
const clear = new ClearDBData(coin); const clear = new ClearDBData(coin);

View File

@ -41,12 +41,9 @@ class Confirm extends Init {
} else { } else {
const now = Date.now().valueOf() const now = Date.now().valueOf()
const max_mature_time = now - this.MAX_MATURE * 60 * 1000 const max_mature_time = now - this.MAX_MATURE * 60 * 1000
console.log("MAX_MATURE:", this.MAX_MATURE);
console.log("max_mature_time: ", max_mature_time);
const ymd = Times.utcTime(max_mature_time) const ymd = Times.utcTime(max_mature_time)
const sql = `SELECT MAX(height) AS max_height FROM alph_blkreportprofitv2 WHERE date <= ? AND state = ?;` const sql = `SELECT MAX(height) AS max_height FROM alph_blkreportprofitv2 WHERE date <= ? AND state = ?;`
const data = await this.distribution.exec(sql, [ymd, 0]) const data = await this.distribution.exec(sql, [ymd, 0])
console.log("data: ", data);
if(!data[0]){ if(!data[0]){
console.log(`alph当前时间没有需要更新的成熟区块`); console.log(`alph当前时间没有需要更新的成熟区块`);
return false return false
@ -119,13 +116,13 @@ class Confirm extends Init {
suc_heights.push(item); suc_heights.push(item);
} }
} }
if(err_heights.length === 0 && need_update_heights.length !== 0){ // if(err_heights.length === 0 && need_update_heights.length !== 0){
console.log(`${mature_max_height}之前有新报块,且无孤块`); // console.log(`${mature_max_height}之前有新报块,且无孤块`);
} else if(err_heights.length !== 0 && need_update_heights.length === 0){ // } else if(err_heights.length !== 0 && need_update_heights.length === 0){
console.log(`${mature_max_height}之前有新报块,但这些报块都是孤块`); // console.log(`${mature_max_height}之前有新报块,但这些报块都是孤块`);
} else if(err_heights.length !== 0 && need_update_heights.length !== 0){ // } else if(err_heights.length !== 0 && need_update_heights.length !== 0){
console.log(`${mature_max_height}之前有新报块,且其中有孤块`); // console.log(`${mature_max_height}之前有新报块,且其中有孤块`);
} // }
await this.update_blkreporprofit_state(suc_heights, err_heights); await this.update_blkreporprofit_state(suc_heights, err_heights);
return return
} catch (err) { } catch (err) {

View File

@ -27,7 +27,7 @@ class Distribution extends Init {
const result = {}; const result = {};
// 计算每个用户的算力占比 // 计算每个用户的算力占比
for (let { user, mhs24h } of alluser_mhs24h) { for (let { user, mhs24h } of alluser_mhs24h) {
result[user] = (mhs24h / totalHashrate) * hash_percent; result[user] = Number(((mhs24h / totalHashrate) * hash_percent).toFixed(4));
} }
return result; return result;
} }
@ -79,7 +79,7 @@ class Distribution extends Init {
* 校验昨天所有报块是否成熟 * 校验昨天所有报块是否成熟
* @param {Date} start_time 采用yyyy-MM-dd hh:mm:ss格式例如"2024-11-11 00:00:00" * @param {Date} start_time 采用yyyy-MM-dd hh:mm:ss格式例如"2024-11-11 00:00:00"
* @param {Date} end_time 同上 * @param {Date} end_time 同上
* @returns * @returns
*/ */
async query_last_day_if_mature(start_time, end_time) { async query_last_day_if_mature(start_time, end_time) {
try { try {
@ -93,7 +93,7 @@ class Distribution extends Init {
// 动态获取当前小时 // 动态获取当前小时
const currentHour = Number(Times.times()[4]); const currentHour = Number(Times.times()[4]);
if(this.coin === "rxd"){ if (this.coin === "rxd") {
if (currentHour >= 9) { if (currentHour >= 9) {
console.log("已超过凌晨 9 点,停止检查。"); console.log("已超过凌晨 9 点,停止检查。");
return false; return false;
@ -111,10 +111,10 @@ class Distribution extends Init {
throw err; throw err;
} }
} }
/** /**
* 查询当前挖矿账户状态包括起付额和钱包地址 * 查询当前挖矿账户状态包括起付额和钱包地址
* @returns * @returns
*/ */
async query_users_address() { async query_users_address() {
try { try {
@ -142,8 +142,8 @@ class Distribution extends Init {
/** /**
* 校验报块 * 校验报块
* @param {Number} height * @param {Number} height
* @returns * @returns
*/ */
async verify_block(height) { async verify_block(height) {
try { try {
@ -159,7 +159,7 @@ class Distribution extends Init {
/** /**
* 将漏掉的报块插入到数据库中 * 将漏掉的报块插入到数据库中
* @param {Array} data [{time: 1708256800, height: 123456, hash: "0x1234567890", reward: 100, fees: 10}] * @param {Array} data [{time: 1708256800, height: 123456, hash: "0x1234567890", reward: 100, fees: 10}]
*/ */
async insert_blkreportprofit(data) { async insert_blkreportprofit(data) {
try { try {
@ -179,8 +179,8 @@ class Distribution extends Init {
/** /**
* 校验昨日是否有漏掉报块的情况 * 校验昨日是否有漏掉报块的情况
* @param {Date} date 当前时间 * @param {Date} date 当前时间
* @returns * @returns
*/ */
async check_last_data_blk(date) { async check_last_data_blk(date) {
try { try {
@ -196,8 +196,9 @@ class Distribution extends Init {
} }
const heights = []; const heights = [];
const query_blk_pool_sql = `SELECT height FROM ${table_name} WHERE DATE(date) >= ?;`; const query_blk_pool_sql = `SELECT height FROM ${table_name} WHERE DATE(date) >= ?;`;
const [master_data, slave_data] = await Promise.all([this.pooldb.exec(query_blk_pool_sql, [yMd]), this.pooldb_slave.exec(`SELECT height FROM ${this.coin}_pool_blkstats WHERE DATE(date) >= ?;`, [yMd])]) // const [master_data, slave_data] = await Promise.all([this.pooldb.exec(query_blk_pool_sql, [yMd]), this.pooldb_slave.exec(`SELECT height FROM ${this.coin}_pool_blkstats WHERE DATE(date) >= ?;`, [yMd])])
const pool_data = master_data.concat(slave_data); // const pool_data = master_data.concat(slave_data);
const pool_data = await this.pooldb.exec(query_blk_pool_sql, [yMd]);
for (let item of pool_data) { for (let item of pool_data) {
heights.push(item.height); heights.push(item.height);
} }
@ -235,15 +236,15 @@ class Distribution extends Init {
/** /**
* 更新wallet_in表中不满足起付额的用户状态 * 更新wallet_in表中不满足起付额的用户状态
* @param {Array} min_amount [{"user": amount}] * @param {Array} min_amount [{"user": amount}]
* @returns * @returns
*/ */
async update_state(min_amount) { async update_state(min_amount) {
try { try {
const sql = `SELECT user, SUM(amount) AS profit FROM wallet_in WHERE coin = ? AND state = ? GROUP BY user;`; const sql = `SELECT user, SUM(amount) AS profit FROM wallet_in WHERE coin = ? AND state = ? GROUP BY user;`;
const data = await this.distributiondb.exec(sql, [this.coin, 2]); // [] const data = await this.distributiondb.exec(sql, [this.coin, 2]); // []
if (!data || data.length === 0) { if (!data || data.length === 0) {
return return;
} }
const need_update_state = []; const need_update_state = [];
for (let item of data) { for (let item of data) {
@ -268,10 +269,10 @@ class Distribution extends Init {
throw err; throw err;
} }
} }
/** /**
* 将最终分配数据插入wallet_in表中 * 将最终分配数据插入wallet_in表中
* @param {Array} data [{coin, user, address, create_date, should_out_date, max_height, amount, state}] * @param {Array} data [{coin, user, address, create_date, should_out_date, max_height, amount, state}]
*/ */
async insert_wallet_in(data) { async insert_wallet_in(data) {
try { try {
@ -294,7 +295,7 @@ class Distribution extends Init {
if (!_if) { if (!_if) {
return; return;
} }
const check_result = await this.check_last_data_blk(end_time) const check_result = await this.check_last_data_blk(end_time);
if (!check_result) { if (!check_result) {
return; return;
} }
@ -306,37 +307,39 @@ class Distribution extends Init {
console.log("users_address:", users_address); console.log("users_address:", users_address);
return; return;
} }
const reward = (BigInt(last_day_reward[0].reward) * BigInt((1 - this.POOL_FEE) * 10000)) / BigInt(10000);
const min_amount = {}; const min_amount = {};
const score_ratio = this.score(last_day_mhs24h, 1); const score_ratio = this.score(last_day_mhs24h, 1);
const reward = Number(last_day_reward[0].reward) * (1 - this.POOL_FEE);
const max_height = last_day_reward[0].max_height; const max_height = last_day_reward[0].max_height;
let should_out_date; // 实际转账时间 let should_out_date; // 实际转账时间
let accuracy; // user保留小数位数100为2位以此类推 let accuracy; // user保留小数位数100为2位以此类推
let count // pool_account 保留小数位数 let count; // pool_account 保留小数位数
if (this.coin === "nexa") { if (this.coin === "nexa") {
should_out_date = Times.utcTime(new Date(end_time).valueOf() + 1000 * 60 * 60 * 24 * 7); should_out_date = Times.utcTime(new Date(end_time).valueOf() + 1000 * 60 * 60 * 24 * 7);
accuracy = 100; accuracy = 100;
count = 2 count = 2;
} else if (this.coin === "rxd") { } else if (this.coin === "rxd") {
accuracy = 100; accuracy = 100;
should_out_date = end_time; should_out_date = end_time;
count = 2 count = 2;
} else if(this.coin === "alph"){ } else if (this.coin === "alph") {
accuracy = 0 accuracy = 0;
should_out_date = end_time; should_out_date = end_time;
count = 0 count = 0;
} else { } else {
should_out_date = end_time; should_out_date = end_time;
accuracy = 100000000; accuracy = 100000000;
count = 8 count = 8;
} }
let user_profit = 0; let user_profit = BigInt(0)
const result = []; const result = [];
let pool_account_address; let pool_account_address;
for (let user in score_ratio) { for (let user in score_ratio) {
const profit = Math.floor(score_ratio[user] * reward * accuracy) / accuracy; // const profit = Math.floor(score_ratio[user] * reward * accuracy) / accuracy;
if(profit === 0){ const ratio = Math.round(score_ratio[user] * 10000);
continue const profit = (reward * BigInt(ratio)) / 10000n;
if (profit === 0) {
continue;
} }
user_profit += profit; user_profit += profit;
for (let item of users_address) { for (let item of users_address) {
@ -377,4 +380,4 @@ class Distribution extends Init {
} }
} }
module.exports = Distribution; module.exports = Distribution;

View File

@ -363,4 +363,39 @@ ON t1.user = t2.user AND t1.miner = t2.miner AND t1.date = t2.max_date;`;
} }
} }
class HashRateNew extends Init {
constructor(coin) {
const method = "distribution";
super(coin, method);
this.count = 0
}
async query_blk_detail_table_name() {
try{
const sql = `SELECT \`from\`, \`to\` FROM ${this.coin}_blk_height_detail WHERE date < NOW() - INTERVAL 5 MINUTE;`
const data = await this.sharesdb.exec(sql);
let tables = [`${this.coin}_blk_detail`]
if(data.length === 0){
return tables
}
for(let item of data){
tables.push(`${this.coin}_block_detail_${item.from}_${Math.trunc(item.to - 1)}`)
}
return tables
} catch(err){
throw err;
}
}
async query_blk_detail(){
try{
} catch(err){
throw err
}
}
}
module.exports = HashRate module.exports = HashRate
// SELECT COUNT(*)
// FROM information_schema.tables
// WHERE table_schema = 'alphsharesdb'
// AND table_name = 'alph_blk_detail';

View File

@ -1,112 +0,0 @@
const Times = require("../public/times");
const Init = require("./init");
/**
* earliest date : 2024-11-26 00:00:00
*/
class Stats extends Init {
constructor(coin) {
const method = "stats";
super(coin, method);
}
async query_pooldb_mhs24h(date) {
try {
const yMd = Times.utcTime(new Date(date).valueOf() - 1000 * 60 * 60 * 24).split(" ")[0];
const table_date = yMd.replace(/\-/g, "");
let table = `${this.coin}_miners_stats_${table_date}`;
console.log(table);
const sql = `SELECT user, SUM(mhs24h) AS mhs24h FROM ${table} WHERE date >= ? AND date <= ? GROUP BY user;`;
const start = yMd + " 23:55:00";
const end = yMd + " 23:59:59";
const data = await this.pooldb.exec(sql, [start, end]);
if (data.length === 0) {
return false;
}
const result = [];
for (let item of data) {
const { user, mhs24h } = item;
result.push({ user, mhs24h: Number(mhs24h) });
}
return result;
} catch (err) {
throw err;
}
}
async query_hashratedb_mhs24h(date) {
try {
const sql = `SELECT user, SUM(mhs24h) AS mhs24h FROM ${this.coin}_mhsv2 WHERE date = ? GROUP BY user;`;
const data = await this.hashratedb.exec(sql, [date]);
if (!data || data.length === 0) {
return false;
}
const result = [];
for (let item of data) {
const { user, mhs24h } = item;
result.push({ user, mhs24h: Number(mhs24h) });
}
return result;
} catch (err) {
throw err;
}
}
async query_distribution_data(date) {
try {
const sql = `SELECT user, amount FROM wallet_in WHERE coin = ? AND create_date = ? AND user != "pool_account";`;
const data = await this.distribution.exec(sql, [this.coin, date]);
if (!data || data.length === 0) {
return false;
}
const result = [];
for (let item of data) {
const { user, amount } = item;
result.push({ user, amount: Number(amount) });
}
return result;
} catch (err) {
throw err;
}
}
caculate_mhs24h_percent(data) {
const totalAmount = data.reduce((acc, item) => acc + item.mhs24h, 0);
const result = data.map((item) => ({
user: item.user,
mhs24h: item.mhs24h,
percentage: Number((item.mhs24h / totalAmount).toFixed(4)), // 保留两位小数
}));
return result;
}
caculate_distribution_percent(data) {
const totalAmount = data.reduce((acc, item) => acc + item.amount, 0);
const result = data.map((item) => ({
user: item.user,
should_receive: Number((item.amount / 0.95).toFixed(8)),
actual_receive: item.amount,
percentage: Number(((item.amount / totalAmount)).toFixed(4)), // 保留两位小数
}));
return result;
}
async caculate_user_should_distribution(date) {
try {
const [user_mhs24h, user_distribution] = await Promise.all([this.query_hashratedb_mhs24h(date), this.query_distribution_data(date)]);
const mhs24h_percent = this.caculate_mhs24h_percent(user_mhs24h)
const distribution_percent = this.caculate_distribution_percent(user_distribution)
let fees = 0
for(let item of distribution_percent){
const {user, should_receive, actual_receive, percentage} = item
fees += should_receive - actual_receive
}
console.log(fees);
} catch (err) {
throw err;
}
}
}
module.exports = Stats;