app v1.0
This commit is contained in:
parent
239b06227c
commit
b4acd2aee6
|
@ -0,0 +1,57 @@
|
||||||
|
app module
|
||||||
|
hashrate
|
||||||
|
distribution
|
||||||
|
report_block
|
||||||
|
confirm_block
|
||||||
|
clear
|
||||||
|
balance
|
||||||
|
|
||||||
|
############# start common #############
|
||||||
|
pm2 start app.js --name nexa-hashratev2 -- hashrate nexa
|
||||||
|
pm2 start app.js --name nexa-reportv2 -- report nexa
|
||||||
|
pm2 start app.js --name nexa-confirm -- confirm nexa
|
||||||
|
pm2 start app.js --name nexa-distributionv2 -- distribution nexa
|
||||||
|
pm2 start app.js --name email -- notice nexa
|
||||||
|
pm2 start app.js --name nexa-balance -- balance nexa
|
||||||
|
|
||||||
|
pm2 start app.js --name grs-hashratev2 -- hashrate grs
|
||||||
|
pm2 start app.js --name grs-reportv2 -- report grs
|
||||||
|
pm2 start app.js --name grs-confirm -- confirm grs
|
||||||
|
pm2 start app.js --name grs-distributionv2 -- distribution grs
|
||||||
|
pm2 start app.js --name grs-balance -- balance grs
|
||||||
|
|
||||||
|
pm2 start app.js --name mona-hashratev2 -- hashrate mona
|
||||||
|
pm2 start app.js --name mona-reportv2 -- report mona
|
||||||
|
pm2 start app.js --name mona-confirm -- confirm mona
|
||||||
|
pm2 start app.js --name mona-distributionv2 -- distribution mona
|
||||||
|
pm2 start app.js --name mona-balance -- balance mona
|
||||||
|
|
||||||
|
pm2 start app.js --name dgbs-hashratev2 -- hashrate 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-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-reportv2 -- report 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-balance -- balance dgbq
|
||||||
|
|
||||||
|
pm2 start app.js --name dgbo-hashratev2 -- hashrate dgbo
|
||||||
|
pm2 start app.js --name dgbo-reportv2 -- report dgbo
|
||||||
|
pm2 start app.js --name dgbo-confirm -- confirm dgbo
|
||||||
|
pm2 start app.js --name dgbo-distributionv2 -- distribution dgbo
|
||||||
|
pm2 start app.js --name dgb-balance -- balance dgbo
|
||||||
|
|
||||||
|
pm2 start app.js --name rxd-hashratev2 -- hashrate rxd
|
||||||
|
pm2 start app.js --name rxd-reportv2 -- report rxd
|
||||||
|
pm2 start app.js --name rxd-confirm -- confirm rxd
|
||||||
|
pm2 start app.js --name rxd-distributionv2 -- distribution rxd
|
||||||
|
pm2 start app.js --name rxd-balance -- balance rxd
|
||||||
|
|
||||||
|
pm2 start app.js --name enx-hashrate -- hashrate enx
|
||||||
|
pm2 start app.js --name enx-report -- report enx
|
||||||
|
|
||||||
|
pm2 start app.js --name alph-report -- report alph
|
||||||
|
pm2 start app.js --name alph-hashrate -- hashrate alph
|
|
@ -0,0 +1,189 @@
|
||||||
|
const schedule = require("node-schedule");
|
||||||
|
// const executeWithRetry = require("./public/retry")
|
||||||
|
const Times = require("./public/times");
|
||||||
|
const HashRate = require("./src/hashrate");
|
||||||
|
const {Report, ReportEnx} = require("./src/report");
|
||||||
|
const Confirm = require("./src/confirm");
|
||||||
|
const Distribution = require("./src/distribution");
|
||||||
|
const { Balance, DGBBlance } = require("./src/blanace");
|
||||||
|
const Stats = require("./src/stat");
|
||||||
|
const ClearDBData = require("./src/clear")
|
||||||
|
const Notice = require("./src/notice");
|
||||||
|
|
||||||
|
const method = process.argv[2];
|
||||||
|
const methods = ["hashrate", "report", "clear", "distribution", "confirm", "balance", "stats", "notice"];
|
||||||
|
|
||||||
|
if (!methods.includes(method)) {
|
||||||
|
throw `暂不支持${method}方法`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const coin = process.argv[3];
|
||||||
|
const coins = ["nexa", "mona", "grs", "dgbq", "dgbs", "dgbo", "rxd", "enx", "alph"];
|
||||||
|
if (!coins.includes(coin)) {
|
||||||
|
throw `暂不支持${coin}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method === "hashrate") {
|
||||||
|
const hashrate = new HashRate(coin);
|
||||||
|
schedule.scheduleJob({ minute: [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55], second: [30] }, async () => {
|
||||||
|
const ymd_now = Times.utcTime(Date.now().valueOf());
|
||||||
|
const ymd = ymd_now.split(":");
|
||||||
|
const end_time = ymd[0] + ":" + ymd[1] + ":" + "00";
|
||||||
|
await hashrate.insert_hashrate_miners_table(end_time);
|
||||||
|
const currentMinute = new Date().getMinutes();
|
||||||
|
|
||||||
|
const data = await hashrate.query_hashrate_miners_accepts(end_time);
|
||||||
|
if (currentMinute === 0 || currentMinute === 30) {
|
||||||
|
await hashrate.insert_mhs(data);
|
||||||
|
await hashrate.insert_mhs_real(data);
|
||||||
|
} else {
|
||||||
|
await hashrate.insert_mhs_real(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method === "report") {
|
||||||
|
let report
|
||||||
|
if(coin === "enx"){
|
||||||
|
report = new ReportEnx(coin);
|
||||||
|
} else {
|
||||||
|
report = new Report(coin);
|
||||||
|
}
|
||||||
|
let interval = 60000;
|
||||||
|
if (coin === "rxd") {
|
||||||
|
interval = 300000;
|
||||||
|
} else if (coin === "nexa") {
|
||||||
|
interval = 120000;
|
||||||
|
}
|
||||||
|
setInterval(() => {
|
||||||
|
report.main();
|
||||||
|
}, interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (method === "confirm") {
|
||||||
|
// // schedule.scheduleJob({hour:[1], minute:[30], second:[0]}, async () =>{
|
||||||
|
// // confirm.main();
|
||||||
|
// // })
|
||||||
|
// let interval = 600000;
|
||||||
|
// if (coin === "rxd") {
|
||||||
|
// interval = 1800000;
|
||||||
|
// }
|
||||||
|
// const confirm = new Confirm(coin);
|
||||||
|
// setInterval(() => {
|
||||||
|
// 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") {
|
||||||
|
const distribution = new Distribution(coin);
|
||||||
|
schedule.scheduleJob({ hour: [0], minute: [1], second: [0] }, async () => {
|
||||||
|
const now_ts = Date.now().valueOf();
|
||||||
|
const last_ts = now_ts - 1000 * 60 * 60 * 24;
|
||||||
|
const ymd_now = Times.utcTime(now_ts);
|
||||||
|
const ymd_last = Times.utcTime(last_ts);
|
||||||
|
const end_time = ymd_now.split(" ")[0] + " 00:00:00";
|
||||||
|
const start_time = ymd_last.split(" ")[0] + " 00:00:00";
|
||||||
|
await distribution.main(start_time, end_time);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (method === "balance") {
|
||||||
|
// const special_coins = ["dgbo", "dgbs", "dgbq"];
|
||||||
|
// let balance;
|
||||||
|
// if (special_coins.includes(coin)) {
|
||||||
|
// balance = new DGBBlance(coin);
|
||||||
|
// } else {
|
||||||
|
// balance = new Balance(coin);
|
||||||
|
// }
|
||||||
|
// balance
|
||||||
|
// .main()
|
||||||
|
// .then(() => {
|
||||||
|
// console.log("ok");
|
||||||
|
// })
|
||||||
|
// .catch((err) => {
|
||||||
|
// console.log(err);
|
||||||
|
// })
|
||||||
|
// .finally(() => {
|
||||||
|
// process.exit(0);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
if (method === "balance") {
|
||||||
|
const special_coins = ["dgbo", "dgbs", "dgbq"];
|
||||||
|
let balance;
|
||||||
|
if (special_coins.includes(coin)) {
|
||||||
|
balance = new DGBBlance(coin);
|
||||||
|
} else {
|
||||||
|
balance = new Balance(coin);
|
||||||
|
}
|
||||||
|
|
||||||
|
let hour = 4
|
||||||
|
if(coin === "rxd"){
|
||||||
|
hour = 9
|
||||||
|
}
|
||||||
|
|
||||||
|
async function task(balance) {
|
||||||
|
let count = 0; // 让 count 成为 task 内部变量,避免多个 schedule 共享 count
|
||||||
|
const last_height = await balance.node.getblockcount()
|
||||||
|
while (count < 36) { // 最多执行 36 次 (6小时)
|
||||||
|
const enable = await balance.query_now_height(last_height);
|
||||||
|
if (enable) {
|
||||||
|
await balance.main();
|
||||||
|
console.log(`${coin}转账已完成`);
|
||||||
|
return; // 成功执行后退出循环
|
||||||
|
}
|
||||||
|
console.log(`等待中... (已等待 ${count * 10} 分钟)`);
|
||||||
|
await balance.sleep(1000 * 60 * 10); // 休眠 10 分钟
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
console.log("等待超时,任务结束!");
|
||||||
|
}
|
||||||
|
schedule.scheduleJob({hour: [hour], minute: [10], second: [0]}, async () => {
|
||||||
|
await task(balance);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (method === "stats") {
|
||||||
|
const stats = new Stats(coin);
|
||||||
|
stats.caculate_user_should_distribution("2024-11-28 00:00:00");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method === "clear") {
|
||||||
|
const clear = new ClearDBData(coin);
|
||||||
|
clear
|
||||||
|
.clearSharesDB(72)
|
||||||
|
.then((res) => {
|
||||||
|
console.log("sharesdb:ok");
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
clear.clearHashrateDB();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
console.log("hashratedb:ok");
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
// clear.clearPoolDB("2024-10-11 00:00:00", "2024-12-23 00:00:00")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method === "notice") {
|
||||||
|
const notice = new Notice(coin);
|
||||||
|
schedule.scheduleJob({hour:[9], minute:[30], second:[0]}, () =>{
|
||||||
|
notice.main()
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
{
|
||||||
|
"master":{
|
||||||
|
"pooldb":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "alphpooldb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"sharesdb":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "alphsharesdb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"distribution":{
|
||||||
|
"host": "13.213.4.56",
|
||||||
|
"user": "pool190",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "distribution",
|
||||||
|
"port":6789,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"hashrate":{
|
||||||
|
"host": "13.213.4.56",
|
||||||
|
"user": "pool190",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "hashrate",
|
||||||
|
"port":6789,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"users_addresses":{
|
||||||
|
"host": "13.214.175.13",
|
||||||
|
"user": "root",
|
||||||
|
"password": "m2Pool2024@!",
|
||||||
|
"database": "pool",
|
||||||
|
"port":3123,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"balance":{
|
||||||
|
"host": "18.143.153.132",
|
||||||
|
"user": "app_user",
|
||||||
|
"password": "8xdZ3~FR$c1mqcxwmLs",
|
||||||
|
"database": "mydb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"slave":{
|
||||||
|
"pooldb_slave":{
|
||||||
|
"host": "13.213.4.56",
|
||||||
|
"user": "pool190",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "distribution",
|
||||||
|
"port":6789,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"sharesdb_slave":{
|
||||||
|
"host": "13.214.176.64",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "dgbosharesdb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"redis_options":{
|
||||||
|
"redis1":{
|
||||||
|
"host":"localhost",
|
||||||
|
"port":6379,
|
||||||
|
"db":9,
|
||||||
|
"connectTimeout":10000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_options":{
|
||||||
|
"node1":{
|
||||||
|
"rpcUser":"test",
|
||||||
|
"rpcPassword":"0x09e220e226f2feb7a971a2b6f958e7d4b1c187c8",
|
||||||
|
"rpcPort":12973,
|
||||||
|
"rpcHost":"127.0.0.1"
|
||||||
|
},
|
||||||
|
"node2":{
|
||||||
|
"rpcUser":"test",
|
||||||
|
"rpcPassword":"0x09e220e226f2feb7a971a2b6f958e7d4b1c187c8",
|
||||||
|
"rpcPort":12973,
|
||||||
|
"rpcHost":"127.0.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"retry_options":{
|
||||||
|
"node":{
|
||||||
|
"max_retries":10,
|
||||||
|
"retry_delay":10000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"REPORT_ADDRESS":"1CYKPymfTVex9KZ2i48S3v5cAE7xT6hERG1P6GJiHgWJu",
|
||||||
|
"MAX_MATURE":500,
|
||||||
|
"distribution_conf":{
|
||||||
|
"PPLNS_SIZE":20000,
|
||||||
|
"MODEL_PERCENT":{
|
||||||
|
"SCORE":0,
|
||||||
|
"PPLNS":0.7,
|
||||||
|
"PROPDIF":0.3
|
||||||
|
},
|
||||||
|
"SCORE_PERCENT":{
|
||||||
|
"HASHRATE":1,
|
||||||
|
"STDDEVS":0
|
||||||
|
},
|
||||||
|
"POOL_FEE":0.025
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
{
|
||||||
|
"master":{
|
||||||
|
"pooldb":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "dgbopooldb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"sharesdb":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "dgbosharesdb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"distribution":{
|
||||||
|
"host": "13.213.4.56",
|
||||||
|
"user": "pool190",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "distribution",
|
||||||
|
"port":6789,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"hashrate":{
|
||||||
|
"host": "13.213.4.56",
|
||||||
|
"user": "pool190",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "hashrate",
|
||||||
|
"port":6789,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"users_addresses":{
|
||||||
|
"host": "13.214.175.13",
|
||||||
|
"user": "root",
|
||||||
|
"password": "m2Pool2024@!",
|
||||||
|
"database": "pool",
|
||||||
|
"port":3123,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"balance":{
|
||||||
|
"host": "18.143.153.132",
|
||||||
|
"user": "app_user",
|
||||||
|
"password": "8xdZ3~FR$c1mqcxwmLs",
|
||||||
|
"database": "mydb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"slave":{
|
||||||
|
"pooldb_slave":{
|
||||||
|
"host": "13.214.176.64",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "dgbopooldb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"sharesdb_slave":{
|
||||||
|
"host": "13.214.176.64",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "dgbosharesdb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"redis_options":{
|
||||||
|
"redis1":{
|
||||||
|
"host":"localhost",
|
||||||
|
"port":6379,
|
||||||
|
"connectTimeout":10000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_options":{
|
||||||
|
"node1":{
|
||||||
|
"rpcUser":"test",
|
||||||
|
"rpcPassword":"test",
|
||||||
|
"rpcPort":14022,
|
||||||
|
"rpcHost":"127.0.0.1"
|
||||||
|
},
|
||||||
|
"node2":{
|
||||||
|
"rpcUser":"test",
|
||||||
|
"rpcPassword":"test",
|
||||||
|
"rpcPort":14022,
|
||||||
|
"rpcHost":"13.214.176.64"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"retry_options":{
|
||||||
|
"node":{
|
||||||
|
"max_retries":10,
|
||||||
|
"retry_delay":10000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"REPORT_ADDRESS":"DSdh4rXmRZizpZh7zKGSsyMqHmFE137G96",
|
||||||
|
"MAX_MATURE":50,
|
||||||
|
"distribution_conf":{
|
||||||
|
"PPLNS_SIZE":20000,
|
||||||
|
"MODEL_PERCENT":{
|
||||||
|
"SCORE":0,
|
||||||
|
"PPLNS":0.7,
|
||||||
|
"PROPDIF":0.3
|
||||||
|
},
|
||||||
|
"SCORE_PERCENT":{
|
||||||
|
"HASHRATE":1,
|
||||||
|
"STDDEVS":0
|
||||||
|
},
|
||||||
|
"POOL_FEE":0.025
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
{
|
||||||
|
"master":{
|
||||||
|
"pooldb":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "dgbqpooldb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"sharesdb":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "dgbqsharesdb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"distribution":{
|
||||||
|
"host": "13.213.4.56",
|
||||||
|
"user": "pool190",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "distribution",
|
||||||
|
"port":6789,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"hashrate":{
|
||||||
|
"host": "13.213.4.56",
|
||||||
|
"user": "pool190",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "hashrate",
|
||||||
|
"port":6789,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"users_addresses":{
|
||||||
|
"host": "13.214.175.13",
|
||||||
|
"user": "root",
|
||||||
|
"password": "m2Pool2024@!",
|
||||||
|
"database": "pool",
|
||||||
|
"port":3123,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"balance":{
|
||||||
|
"host": "18.143.153.132",
|
||||||
|
"user": "app_user",
|
||||||
|
"password": "8xdZ3~FR$c1mqcxwmLs",
|
||||||
|
"database": "mydb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"slave":{
|
||||||
|
"pooldb_slave":{
|
||||||
|
"host": "13.214.176.64",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "dgbqpooldb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"sharesdb_slave":{
|
||||||
|
"host": "13.214.176.64",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "dgbqsharesdb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"redis_options":{
|
||||||
|
"redis1":{
|
||||||
|
"host":"localhost",
|
||||||
|
"port":6379,
|
||||||
|
"connectTimeout":10000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_options":{
|
||||||
|
"node1":{
|
||||||
|
"rpcUser":"test",
|
||||||
|
"rpcPassword":"test",
|
||||||
|
"rpcPort":14022,
|
||||||
|
"rpcHost":"127.0.0.1"
|
||||||
|
},
|
||||||
|
"node2":{
|
||||||
|
"rpcUser":"test",
|
||||||
|
"rpcPassword":"test",
|
||||||
|
"rpcPort":14022,
|
||||||
|
"rpcHost":"13.214.176.64"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"retry_options":{
|
||||||
|
"node":{
|
||||||
|
"max_retries":10,
|
||||||
|
"retry_delay":10000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"REPORT_ADDRESS":"DK8ZTp89aahJpmnwdZ8LNsAUNoGA6qS43G",
|
||||||
|
"MAX_MATURE":50,
|
||||||
|
"distribution_conf":{
|
||||||
|
"PPLNS_SIZE":20000,
|
||||||
|
"MODEL_PERCENT":{
|
||||||
|
"SCORE":0,
|
||||||
|
"PPLNS":0.7,
|
||||||
|
"PROPDIF":0.3
|
||||||
|
},
|
||||||
|
"SCORE_PERCENT":{
|
||||||
|
"HASHRATE":1,
|
||||||
|
"STDDEVS":0
|
||||||
|
},
|
||||||
|
"POOL_FEE":0.025
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
{
|
||||||
|
"master":{
|
||||||
|
"pooldb":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "dgbspooldb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"sharesdb":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "dgbssharesdb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"distribution":{
|
||||||
|
"host": "13.213.4.56",
|
||||||
|
"user": "pool190",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "distribution",
|
||||||
|
"port":6789,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"hashrate":{
|
||||||
|
"host": "13.213.4.56",
|
||||||
|
"user": "pool190",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "hashrate",
|
||||||
|
"port":6789,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"users_addresses":{
|
||||||
|
"host": "13.214.175.13",
|
||||||
|
"user": "root",
|
||||||
|
"password": "m2Pool2024@!",
|
||||||
|
"database": "pool",
|
||||||
|
"port":3123,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"balance":{
|
||||||
|
"host": "18.143.153.132",
|
||||||
|
"user": "app_user",
|
||||||
|
"password": "8xdZ3~FR$c1mqcxwmLs",
|
||||||
|
"database": "mydb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"slave":{
|
||||||
|
"pooldb_slave":{
|
||||||
|
"host": "13.214.176.64",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "dgbspooldb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"sharesdb_slave":{
|
||||||
|
"host": "13.214.176.64",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "dgbssharesdb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"redis_options":{
|
||||||
|
"redis1":{
|
||||||
|
"host":"localhost",
|
||||||
|
"port":6379,
|
||||||
|
"connectTimeout":10000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_options":{
|
||||||
|
"node1":{
|
||||||
|
"rpcUser":"test",
|
||||||
|
"rpcPassword":"test",
|
||||||
|
"rpcPort":14022,
|
||||||
|
"rpcHost":"127.0.0.1"
|
||||||
|
},
|
||||||
|
"node2":{
|
||||||
|
"rpcUser":"test",
|
||||||
|
"rpcPassword":"test",
|
||||||
|
"rpcPort":14022,
|
||||||
|
"rpcHost":"13.214.176.64"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"retry_options":{
|
||||||
|
"node":{
|
||||||
|
"max_retries":10,
|
||||||
|
"retry_delay":10000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"REPORT_ADDRESS":"DDQVUDkCNfF5TKjJ5n6jh7BG2wZxr9SNnq",
|
||||||
|
"MAX_MATURE":50,
|
||||||
|
"distribution_conf":{
|
||||||
|
"PPLNS_SIZE":20000,
|
||||||
|
"MODEL_PERCENT":{
|
||||||
|
"SCORE":0,
|
||||||
|
"PPLNS":0.7,
|
||||||
|
"PROPDIF":0.3
|
||||||
|
},
|
||||||
|
"SCORE_PERCENT":{
|
||||||
|
"HASHRATE":1,
|
||||||
|
"STDDEVS":0
|
||||||
|
},
|
||||||
|
"POOL_FEE":0.025
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
{
|
||||||
|
"master":{
|
||||||
|
"pooldb":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "enxpooldb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"sharesdb":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "enxsharesdb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"distribution":{
|
||||||
|
"host": "13.213.4.56",
|
||||||
|
"user": "pool190",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "distribution",
|
||||||
|
"port":6789,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"hashrate":{
|
||||||
|
"host": "13.213.4.56",
|
||||||
|
"user": "pool190",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "hashrate",
|
||||||
|
"port":6789,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"users_addresses":{
|
||||||
|
"host": "13.214.175.13",
|
||||||
|
"user": "root",
|
||||||
|
"password": "m2Pool2024@!",
|
||||||
|
"database": "pool",
|
||||||
|
"port":3123,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"balance":{
|
||||||
|
"host": "18.143.153.132",
|
||||||
|
"user": "app_user",
|
||||||
|
"password": "8xdZ3~FR$c1mqcxwmLs",
|
||||||
|
"database": "mydb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"slave":{
|
||||||
|
"pooldb_slave":{
|
||||||
|
"host": "13.214.176.64",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "dgbopooldb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"sharesdb_slave":{
|
||||||
|
"host": "13.214.176.64",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "dgbosharesdb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"redis_options":{
|
||||||
|
"redis1":{
|
||||||
|
"host":"localhost",
|
||||||
|
"port":6379,
|
||||||
|
"connectTimeout":10000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_options":{
|
||||||
|
"node1":{
|
||||||
|
"rpcUser":"test",
|
||||||
|
"rpcPassword":"test",
|
||||||
|
"rpcPort":16110,
|
||||||
|
"rpcHost":"10.168.1.162"
|
||||||
|
},
|
||||||
|
"node2":{
|
||||||
|
"rpcUser":"test",
|
||||||
|
"rpcPassword":"test",
|
||||||
|
"rpcPort":16110,
|
||||||
|
"rpcHost":"10.168.1.162"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"retry_options":{
|
||||||
|
"node":{
|
||||||
|
"max_retries":10,
|
||||||
|
"retry_delay":10000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"REPORT_ADDRESS":"entropyx:qpe7q43ajzuv42az6kjdu84yrzdhzj33w03kpytfg5lx5yk3x0pd6x0r87hhr",
|
||||||
|
"MAX_MATURE":1000,
|
||||||
|
"distribution_conf":{
|
||||||
|
"PPLNS_SIZE":20000,
|
||||||
|
"MODEL_PERCENT":{
|
||||||
|
"SCORE":0,
|
||||||
|
"PPLNS":0.7,
|
||||||
|
"PROPDIF":0.3
|
||||||
|
},
|
||||||
|
"SCORE_PERCENT":{
|
||||||
|
"HASHRATE":1,
|
||||||
|
"STDDEVS":0
|
||||||
|
},
|
||||||
|
"POOL_FEE":0
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
{
|
||||||
|
"master":{
|
||||||
|
"pooldb":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "grspooldb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"sharesdb":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "grssharesdb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"distribution":{
|
||||||
|
"host": "13.213.4.56",
|
||||||
|
"user": "pool190",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "distribution",
|
||||||
|
"port":6789,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"hashrate":{
|
||||||
|
"host": "13.213.4.56",
|
||||||
|
"user": "pool190",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "hashrate",
|
||||||
|
"port":6789,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"users_addresses":{
|
||||||
|
"host": "13.214.175.13",
|
||||||
|
"user": "root",
|
||||||
|
"password": "m2Pool2024@!",
|
||||||
|
"database": "pool",
|
||||||
|
"port":3123,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"balance":{
|
||||||
|
"host": "18.143.153.132",
|
||||||
|
"user": "app_user",
|
||||||
|
"password": "8xdZ3~FR$c1mqcxwmLs",
|
||||||
|
"database": "mydb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"slave":{
|
||||||
|
"pooldb_slave":{
|
||||||
|
"host": "172.17.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "grspooldb",
|
||||||
|
"port":3307,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"sharesdb_slave":{
|
||||||
|
"host": "172.17.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "grssharesdb",
|
||||||
|
"port":3307,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"redis_options":{
|
||||||
|
"redis1":{
|
||||||
|
"host":"localhost",
|
||||||
|
"port":6379,
|
||||||
|
"connectTimeout":10000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_options":{
|
||||||
|
"node1":{
|
||||||
|
"rpcUser":"test",
|
||||||
|
"rpcPassword":"test",
|
||||||
|
"rpcPort":1441,
|
||||||
|
"rpcHost":"127.0.0.1"
|
||||||
|
},
|
||||||
|
"node2":{
|
||||||
|
"rpcUser":"test",
|
||||||
|
"rpcPassword":"test",
|
||||||
|
"rpcPort":1441,
|
||||||
|
"rpcHost":"13.213.4.56"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"retry_options":{
|
||||||
|
"node":{
|
||||||
|
"max_retries":10,
|
||||||
|
"retry_delay":10000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"REPORT_ADDRESS":"3BZgQAB1tAN6zRcseavQ6vcMtXXiwThTvL",
|
||||||
|
"MAX_MATURE":20,
|
||||||
|
"distribution_conf":{
|
||||||
|
"PPLNS_SIZE":20000,
|
||||||
|
"MODEL_PERCENT":{
|
||||||
|
"SCORE":0,
|
||||||
|
"PPLNS":0.7,
|
||||||
|
"PROPDIF":0.3
|
||||||
|
},
|
||||||
|
"SCORE_PERCENT":{
|
||||||
|
"HASHRATE":1,
|
||||||
|
"STDDEVS":0
|
||||||
|
},
|
||||||
|
"POOL_FEE":0.025
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
{
|
||||||
|
"master":{
|
||||||
|
"pooldb":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "monapooldb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"sharesdb":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "monasharesdb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"distribution":{
|
||||||
|
"host": "13.213.4.56",
|
||||||
|
"user": "pool190",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "distribution",
|
||||||
|
"port":6789,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"hashrate":{
|
||||||
|
"host": "13.213.4.56",
|
||||||
|
"user": "pool190",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "hashrate",
|
||||||
|
"port":6789,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"users_addresses":{
|
||||||
|
"host": "13.214.175.13",
|
||||||
|
"user": "root",
|
||||||
|
"password": "m2Pool2024@!",
|
||||||
|
"database": "pool",
|
||||||
|
"port":3123,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"balance":{
|
||||||
|
"host": "18.143.153.132",
|
||||||
|
"user": "app_user",
|
||||||
|
"password": "8xdZ3~FR$c1mqcxwmLs",
|
||||||
|
"database": "mydb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"slave":{
|
||||||
|
"pooldb_slave":{
|
||||||
|
"host": "172.17.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "monapooldb",
|
||||||
|
"port":3307,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"sharesdb_slave":{
|
||||||
|
"host": "172.17.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "monasharesdb",
|
||||||
|
"port":3307,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"redis_options":{
|
||||||
|
"redis1":{
|
||||||
|
"host":"localhost",
|
||||||
|
"port":6379,
|
||||||
|
"connectTimeout":10000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_options":{
|
||||||
|
"node1":{
|
||||||
|
"rpcUser":"test",
|
||||||
|
"rpcPassword":"test",
|
||||||
|
"rpcPort":9402,
|
||||||
|
"rpcHost":"127.0.0.1"
|
||||||
|
},
|
||||||
|
"node2":{
|
||||||
|
"rpcUser":"test",
|
||||||
|
"rpcPassword":"test",
|
||||||
|
"rpcPort":9402,
|
||||||
|
"rpcHost":"13.213.4.56"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"retry_options":{
|
||||||
|
"node":{
|
||||||
|
"max_retries":10,
|
||||||
|
"retry_delay":10000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"REPORT_ADDRESS":"PSq64UuybtHykZvV9eQa77bkkAZyLgLkcG",
|
||||||
|
"MAX_MATURE":20,
|
||||||
|
"distribution_conf":{
|
||||||
|
"PPLNS_SIZE":20000,
|
||||||
|
"MODEL_PERCENT":{
|
||||||
|
"SCORE":0,
|
||||||
|
"PPLNS":0.7,
|
||||||
|
"PROPDIF":0.3
|
||||||
|
},
|
||||||
|
"SCORE_PERCENT":{
|
||||||
|
"HASHRATE":1,
|
||||||
|
"STDDEVS":0
|
||||||
|
},
|
||||||
|
"POOL_FEE":0.025
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
{
|
||||||
|
"master":{
|
||||||
|
"pooldb":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "m2pooldb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"sharesdb":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "sharesdb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"distribution":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "root",
|
||||||
|
"password": "123456",
|
||||||
|
"database": "distribution",
|
||||||
|
"port":6789,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"hashrate":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "root",
|
||||||
|
"password": "123456",
|
||||||
|
"database": "test",
|
||||||
|
"port":6789,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"users_addresses":{
|
||||||
|
"host": "13.214.175.13",
|
||||||
|
"user": "root",
|
||||||
|
"password": "m2Pool2024@!",
|
||||||
|
"database": "pool",
|
||||||
|
"port":3123,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"balance":{
|
||||||
|
"host": "18.143.153.132",
|
||||||
|
"user": "app_user",
|
||||||
|
"password": "8xdZ3~FR$c1mqcxwmLs",
|
||||||
|
"database": "mydb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"slave":{
|
||||||
|
"pooldb_slave":{
|
||||||
|
"host": "172.17.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "m2pooldb",
|
||||||
|
"port":3307,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"sharesdb_slave":{
|
||||||
|
"host": "172.17.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "sharesdb",
|
||||||
|
"port":3307,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"redis_options":{
|
||||||
|
"redis1":{
|
||||||
|
"host":"localhost",
|
||||||
|
"port":6379,
|
||||||
|
"connectTimeout":10000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_options":{
|
||||||
|
"node1":{
|
||||||
|
"rpcUser":"test",
|
||||||
|
"rpcPassword":"test",
|
||||||
|
"rpcPort":7227,
|
||||||
|
"rpcHost":"127.0.0.1"
|
||||||
|
},
|
||||||
|
"node2":{
|
||||||
|
"rpcUser":"test",
|
||||||
|
"rpcPassword":"test",
|
||||||
|
"rpcPort":7227,
|
||||||
|
"rpcHost":"18.139.85.190"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"retry_options":{
|
||||||
|
"node":{
|
||||||
|
"max_retries":10,
|
||||||
|
"retry_delay":10000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"REPORT_ADDRESS":"nexa:nqtsq5g54692xaxstwa8je7u6g83uy2x0mlgl52w75uvrsg4",
|
||||||
|
"MAX_MATURE":20,
|
||||||
|
"distribution_conf":{
|
||||||
|
"PPLNS_SIZE":20000,
|
||||||
|
"MODEL_PERCENT":{
|
||||||
|
"SCORE":0,
|
||||||
|
"PPLNS":0.7,
|
||||||
|
"PROPDIF":0.3
|
||||||
|
},
|
||||||
|
"SCORE_PERCENT":{
|
||||||
|
"HASHRATE":1,
|
||||||
|
"STDDEVS":0
|
||||||
|
},
|
||||||
|
"POOL_FEE":0.025
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
{
|
||||||
|
"master":{
|
||||||
|
"pooldb":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "rxdpooldb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"sharesdb":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "rxdsharesdb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"distribution":{
|
||||||
|
"host": "13.213.4.56",
|
||||||
|
"user": "pool190",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "distribution",
|
||||||
|
"port":6789,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"hashrate":{
|
||||||
|
"host": "13.213.4.56",
|
||||||
|
"user": "pool190",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "hashrate",
|
||||||
|
"port":6789,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"users_addresses":{
|
||||||
|
"host": "13.214.175.13",
|
||||||
|
"user": "root",
|
||||||
|
"password": "m2Pool2024@!",
|
||||||
|
"database": "pool",
|
||||||
|
"port":3123,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"balance":{
|
||||||
|
"host": "18.143.153.132",
|
||||||
|
"user": "app_user",
|
||||||
|
"password": "8xdZ3~FR$c1mqcxwmLs",
|
||||||
|
"database": "mydb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"slave":{
|
||||||
|
"pooldb_slave":{
|
||||||
|
"host": "172.17.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "dgbopooldb",
|
||||||
|
"port":3307,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"sharesdb_slave":{
|
||||||
|
"host": "172.17.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "dgbosharesdb",
|
||||||
|
"port":3307,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"redis_options":{
|
||||||
|
"redis1":{
|
||||||
|
"host":"localhost",
|
||||||
|
"port":6379,
|
||||||
|
"connectTimeout":10000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_options":{
|
||||||
|
"node1":{
|
||||||
|
"rpcUser":"test",
|
||||||
|
"rpcPassword":"test",
|
||||||
|
"rpcPort":7332,
|
||||||
|
"rpcHost":"127.0.0.1"
|
||||||
|
},
|
||||||
|
"node2":{
|
||||||
|
"rpcUser":"test",
|
||||||
|
"rpcPassword":"test",
|
||||||
|
"rpcPort":7332,
|
||||||
|
"rpcHost":"18.141.161.129"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"retry_options":{
|
||||||
|
"node":{
|
||||||
|
"max_retries":10,
|
||||||
|
"retry_delay":10000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"REPORT_ADDRESS":"1MBm1CZGSxa8DbDRjNVigyYXo24gU3AEfv",
|
||||||
|
"MAX_MATURE":10,
|
||||||
|
"distribution_conf":{
|
||||||
|
"PPLNS_SIZE":20000,
|
||||||
|
"MODEL_PERCENT":{
|
||||||
|
"SCORE":0,
|
||||||
|
"PPLNS":0.7,
|
||||||
|
"PROPDIF":0.3
|
||||||
|
},
|
||||||
|
"SCORE_PERCENT":{
|
||||||
|
"HASHRATE":1,
|
||||||
|
"STDDEVS":0
|
||||||
|
},
|
||||||
|
"POOL_FEE":0.025
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
{
|
||||||
|
"master":{
|
||||||
|
"pooldb":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "m2pooldb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"sharesdb":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "sharesdb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"distribution":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "root",
|
||||||
|
"password": "123456",
|
||||||
|
"database": "distribution",
|
||||||
|
"port":6789,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"hashrate":{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"user": "root",
|
||||||
|
"password": "123456",
|
||||||
|
"database": "test",
|
||||||
|
"port":6789,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"users_addresses":{
|
||||||
|
"host": "13.214.175.13",
|
||||||
|
"user": "root",
|
||||||
|
"password": "m2Pool2024@!",
|
||||||
|
"database": "pool",
|
||||||
|
"port":3123,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"balance":{
|
||||||
|
"host": "18.143.153.132",
|
||||||
|
"user": "app_user",
|
||||||
|
"password": "8xdZ3~FR$c1mqcxwmLs",
|
||||||
|
"database": "mydb",
|
||||||
|
"port":3306,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"slave":{
|
||||||
|
"pooldb_slave":{
|
||||||
|
"host": "172.17.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "m2pooldb",
|
||||||
|
"port":3307,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
},
|
||||||
|
"sharesdb_slave":{
|
||||||
|
"host": "172.17.0.1",
|
||||||
|
"user": "m2pool",
|
||||||
|
"password": "pMJzgwrg@Frt8aDXkQAsTGhG!zy!H8Jd",
|
||||||
|
"database": "sharesdb",
|
||||||
|
"port":3307,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"redis_options":{
|
||||||
|
"redis1":{
|
||||||
|
"host":"localhost",
|
||||||
|
"port":6379,
|
||||||
|
"connectTimeout":10000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_options":{
|
||||||
|
"node1":{
|
||||||
|
"rpcUser":"test",
|
||||||
|
"rpcPassword":"test",
|
||||||
|
"rpcPort":7227,
|
||||||
|
"rpcHost":"127.0.0.1"
|
||||||
|
},
|
||||||
|
"node2":{
|
||||||
|
"rpcUser":"test",
|
||||||
|
"rpcPassword":"test",
|
||||||
|
"rpcPort":7227,
|
||||||
|
"rpcHost":"18.139.85.190"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"retry_options":{
|
||||||
|
"node":{
|
||||||
|
"max_retries":10,
|
||||||
|
"retry_delay":10000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"REPORT_ADDRESS":"nexa:nqtsq5g54692xaxstwa8je7u6g83uy2x0mlgl52w75uvrsg4",
|
||||||
|
"MAX_MATURE":20,
|
||||||
|
"distribution_conf":{
|
||||||
|
"PPLNS_SIZE":20000,
|
||||||
|
"MODEL_PERCENT":{
|
||||||
|
"SCORE":0,
|
||||||
|
"PPLNS":0.7,
|
||||||
|
"PROPDIF":0.3
|
||||||
|
},
|
||||||
|
"SCORE_PERCENT":{
|
||||||
|
"HASHRATE":1,
|
||||||
|
"STDDEVS":0
|
||||||
|
},
|
||||||
|
"POOL_FEE":0.025
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
const mysql = require("mysql2/promise");
|
||||||
|
|
||||||
|
class DBPool {
|
||||||
|
constructor(coin, options) {
|
||||||
|
this.coin = coin;
|
||||||
|
this.pool = mysql.createPool(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 非事务SQL操作
|
||||||
|
* @param {String} sql
|
||||||
|
* @param {Array} values
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async exec(sql, values = []) {
|
||||||
|
const con = await this.pool.getConnection();
|
||||||
|
try {
|
||||||
|
const [data] = await con.query(sql, values);
|
||||||
|
return data;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
con.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 单独SQL事务操作
|
||||||
|
* @param {String} sql
|
||||||
|
* @param {Array} values
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async exec_transaction(sql, values = []) {
|
||||||
|
const con = await this.pool.getConnection();
|
||||||
|
try {
|
||||||
|
await con.beginTransaction();
|
||||||
|
const [data] = await con.query(sql, values);
|
||||||
|
await con.commit();
|
||||||
|
return data;
|
||||||
|
} catch (err) {
|
||||||
|
await con.rollback();
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
con.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行一系列 SQL 操作,合并到一个事务中。
|
||||||
|
* @param {Array} params [{sql:"", param:[param1, param2, ...]}]
|
||||||
|
*/
|
||||||
|
async exec_transaction_together(params) {
|
||||||
|
const con = await this.pool.getConnection();
|
||||||
|
try {
|
||||||
|
await con.beginTransaction();
|
||||||
|
|
||||||
|
// 确保所有查询都完成后再提交事务
|
||||||
|
for (const { sql, param } of params) {
|
||||||
|
await con.query(sql, param);
|
||||||
|
}
|
||||||
|
|
||||||
|
await con.commit();
|
||||||
|
} catch (err) {
|
||||||
|
await con.rollback();
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
con.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async exec_write_lock(sql, values = [], table_name) {
|
||||||
|
const con = await this.pool.getConnection();
|
||||||
|
try {
|
||||||
|
await con.beginTransaction();
|
||||||
|
// 使用模板字符串而不是占位符来构建 LOCK TABLES 语句
|
||||||
|
await con.query(`LOCK TABLES ${table_name} WRITE`);
|
||||||
|
await con.query(sql, values);
|
||||||
|
await con.commit();
|
||||||
|
} catch (err) {
|
||||||
|
await con.rollback();
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
await con.query("UNLOCK TABLES");
|
||||||
|
con.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = DBPool
|
|
@ -0,0 +1,511 @@
|
||||||
|
const axios = require("axios");
|
||||||
|
const ClientWarapper = require("kaspa-rpc-client").ClientWrapper
|
||||||
|
|
||||||
|
class BaseRPCNode {
|
||||||
|
constructor(NODE_OPTION) {
|
||||||
|
const { rpcUser, rpcHost, rpcPassword, rpcPort } = NODE_OPTION;
|
||||||
|
const request_Rpc = axios.create({
|
||||||
|
baseURL: `http://${rpcHost}:${rpcPort}`, // 修正为 baseURL
|
||||||
|
auth: {
|
||||||
|
username: rpcUser,
|
||||||
|
password: rpcPassword,
|
||||||
|
},
|
||||||
|
timeout: 5000, // 超时时间,5秒
|
||||||
|
});
|
||||||
|
this.request_Rpc = request_Rpc;
|
||||||
|
}
|
||||||
|
async callRpcMethod(method, params = []) {
|
||||||
|
try {
|
||||||
|
const response = await this.request_Rpc.post("/", {
|
||||||
|
jsonrpc: "1.0",
|
||||||
|
id: "testnet",
|
||||||
|
method: method,
|
||||||
|
params: params,
|
||||||
|
});
|
||||||
|
return response.data.result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("RPC Error:", error.response ? error.response.data : error.message);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async getblockcount() {
|
||||||
|
try {
|
||||||
|
// const blockhash = await getblockhash(height);
|
||||||
|
const data = await this.callRpcMethod("getblockcount", []);
|
||||||
|
return data;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async getblockhash(height) {
|
||||||
|
try {
|
||||||
|
const data = await this.callRpcMethod("getblockhash", [height]);
|
||||||
|
return data;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async getblock(param) {
|
||||||
|
try {
|
||||||
|
let data;
|
||||||
|
if (typeof param === "string") {
|
||||||
|
data = await this.callRpcMethod("getblock", [param, 2]);
|
||||||
|
} else if (typeof param === "number") {
|
||||||
|
const hash = await this.getblockhash(param);
|
||||||
|
data = await this.callRpcMethod("getblock", [hash, 2]);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NEXARPCNode extends BaseRPCNode {
|
||||||
|
constructor(NODE_OPTION) {
|
||||||
|
super(NODE_OPTION); // 调用父类构造函数
|
||||||
|
}
|
||||||
|
|
||||||
|
async verify_wallet(address) {
|
||||||
|
try {
|
||||||
|
const data = await this.callRpcMethod("getreceivedbyaddress", [address]);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getblock(height) {
|
||||||
|
try {
|
||||||
|
// const blockhash = await getblockhash(height);
|
||||||
|
const data = await this.callRpcMethod("getblock", [height, 2]);
|
||||||
|
return data;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 校验是否是本矿池报块
|
||||||
|
* @param {Number} height 报块高度
|
||||||
|
* @param {String} address 矿池报块钱包地址
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async verify_block(height, address) {
|
||||||
|
try {
|
||||||
|
const block_data = await this.getblock(height);
|
||||||
|
const { tx } = block_data;
|
||||||
|
for (let item of tx) {
|
||||||
|
if (item.vin.length === 0) {
|
||||||
|
const { addresses } = item.vout[0].scriptPubKey;
|
||||||
|
if (address === addresses[0]) {
|
||||||
|
return block_data;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
block(block_data) {
|
||||||
|
try {
|
||||||
|
// const block_data = await this.getblock(height);
|
||||||
|
const { tx, time, hash, height } = block_data;
|
||||||
|
let block_fees = 0;
|
||||||
|
let block_reward = 0;
|
||||||
|
for (let item of tx) {
|
||||||
|
if (item.vin.length === 0) {
|
||||||
|
// coinbase交易
|
||||||
|
block_reward = item.sends;
|
||||||
|
} else {
|
||||||
|
block_fees += item.fee;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const result = { height, hash, time, block_reward, block_fees };
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GRSRPCNode extends BaseRPCNode {
|
||||||
|
constructor(NODE_OPTION) {
|
||||||
|
super(NODE_OPTION); // 调用父类构造函数
|
||||||
|
}
|
||||||
|
async verify_block(_height, REPORT_ADDRESS) {
|
||||||
|
try {
|
||||||
|
const block_data = await this.getblock(_height);
|
||||||
|
const { tx } = block_data;
|
||||||
|
for (let item of tx) {
|
||||||
|
const { vin, vout } = item;
|
||||||
|
if (vin[0].coinbase) {
|
||||||
|
for (let value of vout) {
|
||||||
|
if (value.scriptPubKey.address) {
|
||||||
|
if (value.scriptPubKey.address === REPORT_ADDRESS) {
|
||||||
|
return block_data;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
block(data) {
|
||||||
|
const { hash, tx, height, time } = data;
|
||||||
|
// const littleEndian = Buffer.from(Buffer.from(hash, "hex").reverse());
|
||||||
|
let reward = 0;
|
||||||
|
let fees = 0;
|
||||||
|
for (let item of tx) {
|
||||||
|
const { vin, vout } = item;
|
||||||
|
if (vin[0].coinbase) {
|
||||||
|
for (let value of vout) {
|
||||||
|
reward += value.value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const { fee } = item;
|
||||||
|
fees += fee;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// console.log(littleEndian.toString("hex")," ", hash);
|
||||||
|
|
||||||
|
return { height, hash, time, block_reward: reward, block_fees: fees };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MONARPCNode extends BaseRPCNode {
|
||||||
|
constructor(NODE_OPTION) {
|
||||||
|
super(NODE_OPTION); // 调用父类构造函数
|
||||||
|
}
|
||||||
|
|
||||||
|
async verify_block(_height, REPORT_ADDRESS) {
|
||||||
|
try {
|
||||||
|
const block_data = await this.getblock(_height);
|
||||||
|
const { tx } = block_data;
|
||||||
|
for (let item of tx) {
|
||||||
|
const { vin, vout } = item;
|
||||||
|
if (vin[0].coinbase) {
|
||||||
|
for (let value of vout) {
|
||||||
|
if (value.scriptPubKey.addresses) {
|
||||||
|
if (value.scriptPubKey.addresses["0"] === REPORT_ADDRESS) {
|
||||||
|
return block_data;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
block(data) {
|
||||||
|
const { hash, tx, height, time } = data;
|
||||||
|
|
||||||
|
let reward = 0;
|
||||||
|
for (let item of tx) {
|
||||||
|
const { vin, vout } = item;
|
||||||
|
if (vin[0].coinbase) {
|
||||||
|
for (let value of vout) {
|
||||||
|
if (value.scriptPubKey.addresses) {
|
||||||
|
reward += value.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { height, hash, time, block_reward: reward, block_fees: null };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DGBRPCNode extends BaseRPCNode {
|
||||||
|
constructor(NODE_OPTION) {
|
||||||
|
super(NODE_OPTION); // 调用父类构造函数
|
||||||
|
}
|
||||||
|
|
||||||
|
async verify_block(_height, REPORT_ADDRESS) {
|
||||||
|
try {
|
||||||
|
const block_data = await this.getblock(_height);
|
||||||
|
const { tx, pow_algo } = block_data;
|
||||||
|
// if (algorithm === pow_algo) {
|
||||||
|
for (let item of tx) {
|
||||||
|
const { vin, vout } = item;
|
||||||
|
if (vin[0].coinbase) {
|
||||||
|
for (let value of vout) {
|
||||||
|
if (value.scriptPubKey.addresses) {
|
||||||
|
if (value.scriptPubKey.addresses[0] === REPORT_ADDRESS) {
|
||||||
|
return block_data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async verify_block_with_alogo(_height, REPORT_ADDRESS, algorithm) {
|
||||||
|
try {
|
||||||
|
const block_data = await this.getblock(_height);
|
||||||
|
const { tx, pow_algo } = block_data;
|
||||||
|
if (algorithm === pow_algo) {
|
||||||
|
for (let item of tx) {
|
||||||
|
const { vin, vout } = item;
|
||||||
|
if (vin[0].coinbase) {
|
||||||
|
for (let value of vout) {
|
||||||
|
if (value.scriptPubKey.addresses) {
|
||||||
|
if (value.scriptPubKey.addresses[0] === REPORT_ADDRESS) {
|
||||||
|
return block_data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
block(data) {
|
||||||
|
const { hash, tx, height, time } = data;
|
||||||
|
let reward = 0;
|
||||||
|
for (let item of tx) {
|
||||||
|
const { vin, vout } = item;
|
||||||
|
if (vin[0].coinbase) {
|
||||||
|
for (let value of vout) {
|
||||||
|
if (value.scriptPubKey.addresses) {
|
||||||
|
reward += value.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { height, hash, time, block_reward: reward, block_fees: null };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RXDRPCNode extends BaseRPCNode {
|
||||||
|
constructor(NODE_OPTION) {
|
||||||
|
super(NODE_OPTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
async verify_block(_height, REPORT_ADDRESS) {
|
||||||
|
try {
|
||||||
|
const block_data = await this.getblock(_height);
|
||||||
|
const { tx } = block_data;
|
||||||
|
for (let { vin, vout } of tx) {
|
||||||
|
if (vin[0].coinbase) {
|
||||||
|
for (let { scriptPubKey } of vout) {
|
||||||
|
if (scriptPubKey.addresses[0] === REPORT_ADDRESS) {
|
||||||
|
const hash = await this.getblockhash(_height);
|
||||||
|
const blockstats = await this.callRpcMethod("getblockstats", [hash]);
|
||||||
|
const { blockhash, height, time, subsidy, totalfee } = blockstats;
|
||||||
|
return { height, hash: blockhash, time, block_reward: subsidy + totalfee, block_fees: totalfee };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
block(data) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ENXNode {
|
||||||
|
constructor(NODE_OPTION) {
|
||||||
|
const { rpcHost, rpcPort } = NODE_OPTION;
|
||||||
|
this.rpcHost = rpcHost;
|
||||||
|
this.rpcPort = rpcPort;
|
||||||
|
|
||||||
|
this.state = false
|
||||||
|
}
|
||||||
|
|
||||||
|
async init (){
|
||||||
|
try{
|
||||||
|
const warapper = new ClientWarapper({
|
||||||
|
hosts: [`${this.rpcHost}:${this.rpcPort}`],
|
||||||
|
verbose: true
|
||||||
|
})
|
||||||
|
await warapper.initialize()
|
||||||
|
this.client = await warapper.getClient()
|
||||||
|
this.state = true
|
||||||
|
} catch (err){
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getblockcount(){
|
||||||
|
if(!this.state){
|
||||||
|
console.log("节点未初始化");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try{
|
||||||
|
const data = await this.client.getBlockDagInfo()
|
||||||
|
const {virtualDaaScore} = data
|
||||||
|
return Number(virtualDaaScore)
|
||||||
|
} catch(err){
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Number} start_height
|
||||||
|
* @param {String} REPORT_ADDRESS
|
||||||
|
* @returns
|
||||||
|
*
|
||||||
|
{
|
||||||
|
address: 'entropyx:qpe7q43ajzuv42az6kjdu84yrzdhzj33w03kpytfg5lx5yk3x0pd6x0r87hhr',
|
||||||
|
outpoint: {
|
||||||
|
transactionId: '9341ecd11e3cccb06ff09ad8f604aee80f9777aafa0f8858a5501c2736b31cb2',
|
||||||
|
index: 0
|
||||||
|
},
|
||||||
|
utxoEntry: {
|
||||||
|
amount: '50000000000',
|
||||||
|
scriptPublicKey: {
|
||||||
|
version: 0,
|
||||||
|
scriptPublicKey: '2073e0563d90b8caaba2d5a4de1ea4189b714a3173e3609169453e6a12d133c2ddac'
|
||||||
|
},
|
||||||
|
blockDaaScore: '2776996',
|
||||||
|
isCoinbase: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
async getTransactions(start_height, REPORT_ADDRESS){
|
||||||
|
if(!this.state){
|
||||||
|
console.log("节点未初始化");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try{
|
||||||
|
const {entries} = await this.client.getUtxosByAddresses({addresses:[REPORT_ADDRESS]})
|
||||||
|
const blocks = []
|
||||||
|
for(let item of entries){
|
||||||
|
if(Number(item.utxoEntry.blockDaaScore) > start_height && item.utxoEntry.isCoinbase){
|
||||||
|
blocks.push({
|
||||||
|
height: Number(item.utxoEntry.blockDaaScore),
|
||||||
|
amount: Number(item.utxoEntry.amount) / 10 ** 8,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return blocks
|
||||||
|
} catch(err){
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class HttpNode {
|
||||||
|
constructor(NODE_OPTION){
|
||||||
|
const { rpcHost, rpcPassword, rpcPort } = NODE_OPTION;
|
||||||
|
this.host = rpcHost
|
||||||
|
this.prot = rpcPort
|
||||||
|
this.api_key = rpcPassword
|
||||||
|
}
|
||||||
|
|
||||||
|
async Get(api = "", params = {}){
|
||||||
|
const url = `http://${this.host}:${this.prot}/${api}`
|
||||||
|
try{
|
||||||
|
const res = await axios.get(url, { params, headers: { "x-api-key": this.api_key } })
|
||||||
|
return res.data
|
||||||
|
} catch (err){
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async Post(api = "", params = {}){
|
||||||
|
const url = `http://${this.host}:${this.prot}/${api}`
|
||||||
|
try{
|
||||||
|
const res = await axios.post(url, params, { headers: { "x-api-key": this.api_key } })
|
||||||
|
return res.data
|
||||||
|
} catch (err){
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ALPHRPCNode extends HttpNode {
|
||||||
|
constructor(NODE_OPTION) {
|
||||||
|
super(NODE_OPTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getblockcount(){
|
||||||
|
try{
|
||||||
|
const result = await this.Get("blockflow/chain-info", {fromGroup:0, toGroup:0})
|
||||||
|
return result.currentHeight
|
||||||
|
} catch(err){
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getblockhash(height){
|
||||||
|
try{
|
||||||
|
const result = await this.Get("blockflow/hashes", {fromGroup:0, toGroup:0, height})
|
||||||
|
return result.headers[0]
|
||||||
|
} catch(err){
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getblock(block_hash){
|
||||||
|
try{
|
||||||
|
const result = await this.Get(`blockflow/blocks/${block_hash}`)
|
||||||
|
return result
|
||||||
|
} catch(err){
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async verify_block(height, REPORT_ADDRESS){
|
||||||
|
try{
|
||||||
|
const block_hash = await this.getblockhash(height)
|
||||||
|
const block = await this.getblock(block_hash)
|
||||||
|
const {transactions, timestamp} = block
|
||||||
|
const coinbaseTx = transactions[transactions.length - 1].unsigned
|
||||||
|
if (coinbaseTx.inputs.length === 0) {
|
||||||
|
const {fixedOutputs} = coinbaseTx
|
||||||
|
if(fixedOutputs.length === 1){
|
||||||
|
const {attoAlphAmount, address } = fixedOutputs[0]
|
||||||
|
if(address === REPORT_ADDRESS){
|
||||||
|
return {height, hash: block_hash, time:Math.trunc(timestamp / 1000), block_reward: Number(attoAlphAmount), block_fees: null };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
} catch(err){
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
block(data){
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
NEXARPCNode,
|
||||||
|
GRSRPCNode,
|
||||||
|
MONARPCNode,
|
||||||
|
DGBRPCNode,
|
||||||
|
RXDRPCNode,
|
||||||
|
ENXNode,
|
||||||
|
ALPHRPCNode
|
||||||
|
};
|
|
@ -0,0 +1,26 @@
|
||||||
|
const Redis = require("ioredis");
|
||||||
|
|
||||||
|
class Cache {
|
||||||
|
constructor(options) {
|
||||||
|
this.redis = new Redis(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
async set(key, value) {
|
||||||
|
try {
|
||||||
|
await this.redis.set(key, value);
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async get(key) {
|
||||||
|
try {
|
||||||
|
const data = await this.redis.get(key);
|
||||||
|
return JSON.parse(data);
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Cache;
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit d880f559f8f1171d4ba66d4b6aa06a265a802233
|
|
@ -0,0 +1,28 @@
|
||||||
|
class SuccessResponse {
|
||||||
|
constructor(data) {
|
||||||
|
if (data) {
|
||||||
|
this.code = 0;
|
||||||
|
this.msg = "Success";
|
||||||
|
this.data = data;
|
||||||
|
} else {
|
||||||
|
this.code = 0;
|
||||||
|
this.msg = "Success";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ErrorResponse {
|
||||||
|
constructor(data) {
|
||||||
|
if (data) {
|
||||||
|
this.code = -1;
|
||||||
|
this.msg = "Error";
|
||||||
|
this.data = data;
|
||||||
|
} else {
|
||||||
|
this.code = -1;
|
||||||
|
this.msg = "Error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { SuccessResponse, ErrorResponse };
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
/**
|
||||||
|
* 计算算力占比
|
||||||
|
* @param {Array} list [{user:"user1", mhs24h:100}, {user:"user2", mhs24h:320}]
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
module.exports = (list) => {
|
||||||
|
const total_hashrate = list.reduce((sum, user) => sum + user.mhs24h, 0);
|
||||||
|
const users_weight = list.map((user) => ({ user: user.user, mhs24h: user.mhs24h, weight: user.mhs24h / total_hashrate }));
|
||||||
|
return users_weight
|
||||||
|
};
|
|
@ -0,0 +1,49 @@
|
||||||
|
/**
|
||||||
|
* 截取小数点后dots位
|
||||||
|
* @param {Number} number 要截取的小数
|
||||||
|
* @param {Number} dots 小数点位数
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function truncateToTenDecimals(number, dots) {
|
||||||
|
const str = number.toString();
|
||||||
|
const decimalIndex = str.indexOf(".");
|
||||||
|
// 如果没有小数部分,直接返回原始数值
|
||||||
|
if (decimalIndex === -1) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
// 截取到小数点后 10 位
|
||||||
|
const truncatedStr = str.slice(0, decimalIndex + dots + 1);
|
||||||
|
return Number(truncatedStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据shares队列计算每个user本轮次的分配占比
|
||||||
|
* @param {Array} data [{user:"user1", pool_diff:100, miner_diff:100}, ]
|
||||||
|
* @returns {"1x1":{totalWeight:10, weightRatio:0.1}}
|
||||||
|
*/
|
||||||
|
function calculate_shares_weight(data) {
|
||||||
|
// Step 1: 按照 user 分类计算每个 user 的总 weight
|
||||||
|
const userWeights = data.reduce((acc, item) => {
|
||||||
|
const weight = item.miner_diff / item.pool_diff;
|
||||||
|
if (!acc[item.user]) {
|
||||||
|
acc[item.user] = { totalWeight: 0, data: [] };
|
||||||
|
}
|
||||||
|
acc[item.user].totalWeight += weight;
|
||||||
|
acc[item.user].data.push(item);
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
// Step 2: 计算所有 user 的总 weight
|
||||||
|
const totalWeight = Object.values(userWeights).reduce((acc, user) => acc + user.totalWeight, 0);
|
||||||
|
|
||||||
|
// Step 3: 计算每个 user 的 weight 占总 weight 的比重并返回所需格式
|
||||||
|
const userWeightRatios = Object.keys(userWeights).reduce((acc, user) => {
|
||||||
|
const weightRatio = truncateToTenDecimals(userWeights[user].totalWeight / totalWeight, 10);
|
||||||
|
acc[user] = Number(weightRatio);
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return userWeightRatios;
|
||||||
|
}
|
||||||
|
module.exports = { calculate_shares_weight, truncateToTenDecimals };
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
const changeEndian = (hex) =>{
|
||||||
|
const buffer = Buffer.from(hex, 'hex');
|
||||||
|
const endian = Buffer.from(buffer.reverse())
|
||||||
|
const result = endian.toString("hex")
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
module.exports = changeEndian
|
|
@ -0,0 +1,513 @@
|
||||||
|
-- 矿工历史算力表
|
||||||
|
CREATE TABLE IF NOT EXISTS nexa_mhsv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
mhs30m DECIMAL(32, 6) NOT NULL,
|
||||||
|
mhs24h DECIMAL(32, 6) NOT NULL,
|
||||||
|
state VARCHAR(15) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS nexa_mhs_realv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
mhs30m DECIMAL(32, 6) NOT NULL,
|
||||||
|
mhs24h DECIMAL(32, 6) NOT NULL,
|
||||||
|
state VARCHAR(15) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS nexa_minersv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
accepts DECIMAL(16,8) NOT NULL,
|
||||||
|
state VARCHAR(10) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 矿工历史算力表
|
||||||
|
CREATE TABLE IF NOT EXISTS grs_mhsv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
mhs30m DECIMAL(32, 6) NOT NULL,
|
||||||
|
mhs24h DECIMAL(32, 6) NOT NULL,
|
||||||
|
state VARCHAR(15) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS grs_mhs_realv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
mhs30m DECIMAL(32, 6) NOT NULL,
|
||||||
|
mhs24h DECIMAL(32, 6) NOT NULL,
|
||||||
|
state VARCHAR(15) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS grs_minersv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
accepts DECIMAL(16,8) NOT NULL,
|
||||||
|
state VARCHAR(10) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 矿工历史算力表
|
||||||
|
CREATE TABLE IF NOT EXISTS mona_mhsv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
mhs30m DECIMAL(32, 6) NOT NULL,
|
||||||
|
mhs24h DECIMAL(32, 6) NOT NULL,
|
||||||
|
state VARCHAR(15) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS mona_mhs_realv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
mhs30m DECIMAL(32, 6) NOT NULL,
|
||||||
|
mhs24h DECIMAL(32, 6) NOT NULL,
|
||||||
|
state VARCHAR(15) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS mona_minersv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
accepts DECIMAL(16,8) NOT NULL,
|
||||||
|
state VARCHAR(10) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 矿工历史算力表
|
||||||
|
CREATE TABLE IF NOT EXISTS dgbs_mhsv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
mhs30m DECIMAL(32, 6) NOT NULL,
|
||||||
|
mhs24h DECIMAL(32, 6) NOT NULL,
|
||||||
|
state VARCHAR(15) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS dgbs_mhs_realv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
mhs30m DECIMAL(32, 6) NOT NULL,
|
||||||
|
mhs24h DECIMAL(32, 6) NOT NULL,
|
||||||
|
state VARCHAR(15) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS dgbs_minersv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
accepts DECIMAL(16,8) NOT NULL,
|
||||||
|
state VARCHAR(10) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 矿工历史算力表
|
||||||
|
CREATE TABLE IF NOT EXISTS dgbq_mhsv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
mhs30m DECIMAL(32, 6) NOT NULL,
|
||||||
|
mhs24h DECIMAL(32, 6) NOT NULL,
|
||||||
|
state VARCHAR(15) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS dgbq_mhs_realv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
mhs30m DECIMAL(32, 6) NOT NULL,
|
||||||
|
mhs24h DECIMAL(32, 6) NOT NULL,
|
||||||
|
state VARCHAR(15) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS dgbq_minersv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
accepts DECIMAL(16,8) NOT NULL,
|
||||||
|
state VARCHAR(10) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 矿工历史算力表
|
||||||
|
CREATE TABLE IF NOT EXISTS dgbo_mhsv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
mhs30m DECIMAL(32, 6) NOT NULL,
|
||||||
|
mhs24h DECIMAL(32, 6) NOT NULL,
|
||||||
|
state VARCHAR(15) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS dgbo_mhs_realv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
mhs30m DECIMAL(32, 6) NOT NULL,
|
||||||
|
mhs24h DECIMAL(32, 6) NOT NULL,
|
||||||
|
state VARCHAR(15) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS dgbo_minersv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
accepts DECIMAL(16,8) NOT NULL,
|
||||||
|
state VARCHAR(10) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS nexa_blkreportprofitv2(
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
height INT NOT NULL PRIMARY KEY,
|
||||||
|
hash VARCHAR(255) NOT NULL,
|
||||||
|
reward DECIMAL(18,8) NOT NULL,
|
||||||
|
fees DECIMAL(18,8),
|
||||||
|
state TINYINT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS mona_blkreportprofitv2(
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
height INT NOT NULL PRIMARY KEY,
|
||||||
|
hash VARCHAR(255) NOT NULL,
|
||||||
|
reward DECIMAL(18,8) NOT NULL,
|
||||||
|
fees DECIMAL(18,8),
|
||||||
|
state TINYINT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS grs_blkreportprofitv2(
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
height INT NOT NULL PRIMARY KEY,
|
||||||
|
hash VARCHAR(255) NOT NULL,
|
||||||
|
reward DECIMAL(18,8) NOT NULL,
|
||||||
|
fees DECIMAL(18,8),
|
||||||
|
state TINYINT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS dgbq_blkreportprofitv2(
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
height INT NOT NULL PRIMARY KEY,
|
||||||
|
hash VARCHAR(255) NOT NULL,
|
||||||
|
reward DECIMAL(18,8) NOT NULL,
|
||||||
|
fees DECIMAL(18,8),
|
||||||
|
state TINYINT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS dgbo_blkreportprofitv2(
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
height INT NOT NULL PRIMARY KEY,
|
||||||
|
hash VARCHAR(255) NOT NULL,
|
||||||
|
reward DECIMAL(18,8) NOT NULL,
|
||||||
|
fees DECIMAL(18,8),
|
||||||
|
state TINYINT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS dgbs_blkreportprofitv2(
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
height INT NOT NULL PRIMARY KEY,
|
||||||
|
hash VARCHAR(255) NOT NULL,
|
||||||
|
reward DECIMAL(18,8) NOT NULL,
|
||||||
|
fees DECIMAL(18,8),
|
||||||
|
state TINYINT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- 矿工历史算力表
|
||||||
|
CREATE TABLE IF NOT EXISTS rxd_mhsv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
mhs30m DECIMAL(32, 6) NOT NULL,
|
||||||
|
mhs24h DECIMAL(32, 6) NOT NULL,
|
||||||
|
state VARCHAR(15) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS rxd_mhs_realv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
mhs30m DECIMAL(32, 6) NOT NULL,
|
||||||
|
mhs24h DECIMAL(32, 6) NOT NULL,
|
||||||
|
state VARCHAR(15) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS rxd_minersv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
accepts DECIMAL(16,8) NOT NULL,
|
||||||
|
state VARCHAR(10) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS rxd_blkreportprofitv2(
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
height INT NOT NULL PRIMARY KEY,
|
||||||
|
hash VARCHAR(255) NOT NULL,
|
||||||
|
reward DECIMAL(18,8) NOT NULL,
|
||||||
|
fees DECIMAL(18,8),
|
||||||
|
state TINYINT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 矿工历史算力表
|
||||||
|
CREATE TABLE IF NOT EXISTS enx_mhsv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
mhs30m DECIMAL(32, 6) NOT NULL,
|
||||||
|
mhs24h DECIMAL(32, 6) NOT NULL,
|
||||||
|
state VARCHAR(15) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS enx_mhs_realv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
mhs30m DECIMAL(32, 6) NOT NULL,
|
||||||
|
mhs24h DECIMAL(32, 6) NOT NULL,
|
||||||
|
state VARCHAR(15) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS enx_minersv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
accepts DECIMAL(16,8) NOT NULL,
|
||||||
|
state VARCHAR(10) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS enx_blkreportprofitv2(
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
height INT NOT NULL PRIMARY KEY,
|
||||||
|
hash VARCHAR(255) NOT NULL,
|
||||||
|
reward DECIMAL(18,8) NOT NULL,
|
||||||
|
fees DECIMAL(18,8),
|
||||||
|
state TINYINT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 矿工历史算力表
|
||||||
|
CREATE TABLE IF NOT EXISTS alph_mhsv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
mhs30m DECIMAL(32, 6) NOT NULL,
|
||||||
|
mhs24h DECIMAL(32, 6) NOT NULL,
|
||||||
|
state VARCHAR(15) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS alph_mhs_realv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
mhs30m DECIMAL(32, 6) NOT NULL,
|
||||||
|
mhs24h DECIMAL(32, 6) NOT NULL,
|
||||||
|
state VARCHAR(15) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS alph_minersv2(
|
||||||
|
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
user VARCHAR(64) NOT NULL,
|
||||||
|
miner VARCHAR(64) NOT NULL,
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
accepts DECIMAL(16,8) NOT NULL,
|
||||||
|
state VARCHAR(10) NOT NULL,
|
||||||
|
last_submit DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS alph_blkreportprofitv2(
|
||||||
|
date DATETIME NOT NULL,
|
||||||
|
height INT NOT NULL PRIMARY KEY,
|
||||||
|
hash VARCHAR(255) NOT NULL,
|
||||||
|
reward DECIMAL(32,8) NOT NULL,
|
||||||
|
fees DECIMAL(32,8),
|
||||||
|
state TINYINT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `alph_pool_blkstats` (
|
||||||
|
`id` INT(10) NOT NULL AUTO_INCREMENT,
|
||||||
|
`date` DATETIME NOT NULL,
|
||||||
|
`height` INT(10),
|
||||||
|
`hash` VARCHAR(128),
|
||||||
|
`pow` VARCHAR(128),
|
||||||
|
`net_target` VARCHAR(128),
|
||||||
|
`submit` VARCHAR(64),
|
||||||
|
`success` TINYINT(1),
|
||||||
|
`accepts` DECIMAL(32,6),
|
||||||
|
`rejects` DECIMAL(32,6),
|
||||||
|
`reward` DECIMAL(32,6),
|
||||||
|
`fee` DECIMAL(32,6),
|
||||||
|
`nonce` VARCHAR(64),
|
||||||
|
`subidx` INT(10),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE `alph_miners` (
|
||||||
|
`id` INT(10) NOT NULL AUTO_INCREMENT,
|
||||||
|
`date` DATETIME NOT NULL,
|
||||||
|
`fromip` VARCHAR(64),
|
||||||
|
`state` VARCHAR(64),
|
||||||
|
`online` DATETIME,
|
||||||
|
`offline` DATETIME,
|
||||||
|
`retry` INT(10),
|
||||||
|
`duration` DECIMAL(12,6),
|
||||||
|
`protocol` VARCHAR(64),
|
||||||
|
`user` VARCHAR(128),
|
||||||
|
`miner` VARCHAR(128),
|
||||||
|
`refindex` VARCHAR(128),
|
||||||
|
`diff` DECIMAL(32,6),
|
||||||
|
`height` INT(10),
|
||||||
|
`accepts` DECIMAL(32,6),
|
||||||
|
`rejects` DECIMAL(32,6),
|
||||||
|
`ratio` DECIMAL(32,6),
|
||||||
|
`staleds` DECIMAL(32,6),
|
||||||
|
`lows` DECIMAL(32,6),
|
||||||
|
`duplicates` DECIMAL(32,6),
|
||||||
|
`formats` DECIMAL(32,6),
|
||||||
|
`others` DECIMAL(32,6),
|
||||||
|
`is_disabled` TINYINT(1),
|
||||||
|
`last_submit` DATETIME,
|
||||||
|
`submits` INT(10),
|
||||||
|
`blocks` INT(10),
|
||||||
|
`orphans` INT(10),
|
||||||
|
`orphan_ratio` DECIMAL(32,6),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE `alph_miners_stats` (
|
||||||
|
`id` INT(10) NOT NULL AUTO_INCREMENT,
|
||||||
|
`date` DATETIME NOT NULL,
|
||||||
|
`user` VARCHAR(128),
|
||||||
|
`miner` VARCHAR(128),
|
||||||
|
`refindex` VARCHAR(128),
|
||||||
|
`shares5m` DECIMAL(32,6),
|
||||||
|
`shares15m` DECIMAL(32,6),
|
||||||
|
`shares30m` DECIMAL(32,6),
|
||||||
|
`shares1h` DECIMAL(32,6),
|
||||||
|
`shares3h` DECIMAL(32,6),
|
||||||
|
`shares6h` DECIMAL(32,6),
|
||||||
|
`shares12h` DECIMAL(32,6),
|
||||||
|
`shares24h` DECIMAL(32,6),
|
||||||
|
`shares48h` DECIMAL(32,6),
|
||||||
|
`rejects5m` DECIMAL(32,6),
|
||||||
|
`rejects15m` DECIMAL(32,6),
|
||||||
|
`rejects30m` DECIMAL(32,6),
|
||||||
|
`rejects1h` DECIMAL(32,6),
|
||||||
|
`rejects3h` DECIMAL(32,6),
|
||||||
|
`rejects6h` DECIMAL(32,6),
|
||||||
|
`rejects12h` DECIMAL(32,6),
|
||||||
|
`rejects24h` DECIMAL(32,6),
|
||||||
|
`rejects48h` DECIMAL(32,6),
|
||||||
|
`mhs5m` DECIMAL(32,6),
|
||||||
|
`mhs15m` DECIMAL(32,6),
|
||||||
|
`mhs30m` DECIMAL(32,6),
|
||||||
|
`mhs1h` DECIMAL(32,6),
|
||||||
|
`mhs3h` DECIMAL(32,6),
|
||||||
|
`mhs6h` DECIMAL(32,6),
|
||||||
|
`mhs12h` DECIMAL(32,6),
|
||||||
|
`mhs24h` DECIMAL(32,6),
|
||||||
|
`mhs48h` DECIMAL(32,6),
|
||||||
|
`ratio5m` DECIMAL(32,6),
|
||||||
|
`ratio15m` DECIMAL(32,6),
|
||||||
|
`ratio30m` DECIMAL(32,6),
|
||||||
|
`ratio1h` DECIMAL(32,6),
|
||||||
|
`ratio3h` DECIMAL(32,6),
|
||||||
|
`ratio6h` DECIMAL(32,6),
|
||||||
|
`ratio12h` DECIMAL(32,6),
|
||||||
|
`ratio24h` DECIMAL(32,6),
|
||||||
|
`ratio48h` DECIMAL(32,6),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE `alph_blk_height_detail` (
|
||||||
|
`id` INT(10) NOT NULL AUTO_INCREMENT,
|
||||||
|
`date` DATETIME NOT NULL,
|
||||||
|
`from` INT(10),
|
||||||
|
`to` INT(10),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE `alph_blk_detail` (
|
||||||
|
`id` INT(10) NOT NULL AUTO_INCREMENT,
|
||||||
|
`date` DATETIME NOT NULL,
|
||||||
|
`height` INT(10),
|
||||||
|
`hash` VARCHAR(128),
|
||||||
|
`user` VARCHAR(128),
|
||||||
|
`miner` VARCHAR(128),
|
||||||
|
`refindex` VARCHAR(128),
|
||||||
|
`success` TINYINT(1),
|
||||||
|
`miner_diff` DECIMAL(32,6),
|
||||||
|
`pool_diff` DECIMAL(32,6),
|
||||||
|
`nonce` VARCHAR(64),
|
||||||
|
`subidx` INT(10),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE `alph_blk_new` (
|
||||||
|
`id` INT(10) NOT NULL AUTO_INCREMENT,
|
||||||
|
`date` DATETIME NOT NULL,
|
||||||
|
`height` INT(10),
|
||||||
|
`hash` VARCHAR(128),
|
||||||
|
`success` TINYINT(1),
|
||||||
|
`nonce` VARCHAR(64),
|
||||||
|
`subidx` INT(10),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
);
|
|
@ -0,0 +1,33 @@
|
||||||
|
/**
|
||||||
|
* 异步任务重试器
|
||||||
|
* @param {Function} task 异步任务
|
||||||
|
* @param {Number} maxRetries 最大重试次数
|
||||||
|
* @param {Number} delay 重试间隔(秒)
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async function executeWithRetry(task, maxRetries, delay) {
|
||||||
|
let attempts = 0;
|
||||||
|
|
||||||
|
while (attempts < maxRetries) {
|
||||||
|
try {
|
||||||
|
// 尝试执行异步任务
|
||||||
|
const result = await task();
|
||||||
|
// console.log("任务成功");
|
||||||
|
return result; // 成功时返回结果
|
||||||
|
} catch (error) {
|
||||||
|
attempts++;
|
||||||
|
console.error(`尝试 ${attempts} 失败:`, error.message);
|
||||||
|
|
||||||
|
if (attempts >= maxRetries) {
|
||||||
|
console.error("已达最大重试次数,任务失败。");
|
||||||
|
throw error; // 达到最大重试次数时抛出错误
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`等待 ${delay} 秒后重试...`);
|
||||||
|
// 等待指定时间后再重试
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, delay * 1000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = executeWithRetry
|
|
@ -0,0 +1,48 @@
|
||||||
|
// 计算标准差
|
||||||
|
function caculate_standar_deviation(data) {
|
||||||
|
// 计算数学期望(均值)
|
||||||
|
const calculateMean = (values) => {
|
||||||
|
const total = values.reduce((acc, value) => acc + parseFloat(value), 0);
|
||||||
|
return total / values.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 计算标准差
|
||||||
|
const calculateStandardDeviation = (values, mean) => {
|
||||||
|
const variance = values.reduce((acc, value) => acc + Math.pow(parseFloat(value) - mean, 2), 0) / values.length;
|
||||||
|
return Math.sqrt(variance);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 计算每个用户的标准差
|
||||||
|
const results = Object.keys(data).reduce((acc, user) => {
|
||||||
|
const values = data[user];
|
||||||
|
const mean = calculateMean(values);
|
||||||
|
const stddev = calculateStandardDeviation(values, mean);
|
||||||
|
acc[user] = stddev;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算每个用户最终得分,满分为100分
|
||||||
|
* @param {Array} alluser_mhs24h 每个用户过去24小时平均算力
|
||||||
|
* @param {Number} hash_percent 24小时平均算力权重
|
||||||
|
* @returns 每个用户最终得分
|
||||||
|
*/
|
||||||
|
function score(alluser_mhs24h, hash_percent = 1) {
|
||||||
|
// 提取 mhs24h 数值
|
||||||
|
const hashrateValues = alluser_mhs24h.map((obj) => obj.mhs24h);
|
||||||
|
|
||||||
|
// 计算总和
|
||||||
|
const totalHashrate = hashrateValues.reduce((sum, value) => sum + value, 0);
|
||||||
|
|
||||||
|
const result = {};
|
||||||
|
|
||||||
|
// 计算每个用户的算力占比
|
||||||
|
for (let { user, mhs24h } of alluser_mhs24h) {
|
||||||
|
result[user] = (mhs24h / totalHashrate) * hash_percent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
module.exports = { caculate_standar_deviation, score };
|
|
@ -0,0 +1,52 @@
|
||||||
|
class Times {
|
||||||
|
static bjTime(dateForm) {
|
||||||
|
if (dateForm === "") {
|
||||||
|
//解决deteForm为空传1970-01-01 00:00:00
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
var dateee = new Date(dateForm).toJSON();
|
||||||
|
var date = new Date(+new Date(dateee) + 8 * 3600 * 1000)
|
||||||
|
.toISOString()
|
||||||
|
.replace(/T/g, " ")
|
||||||
|
.replace(/\.[\d]{3}Z/, "");
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static utcTime(dateForm) {
|
||||||
|
if (dateForm === "") {
|
||||||
|
//解决deteForm为空传1970-01-01 00:00:00
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
var dateee = new Date(dateForm).toJSON();
|
||||||
|
var date = new Date(+new Date(dateee))
|
||||||
|
.toISOString()
|
||||||
|
.replace(/T/g, " ")
|
||||||
|
.replace(/\.[\d]{3}Z/, "");
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static times() {
|
||||||
|
const date = new Date();
|
||||||
|
const y = date.getFullYear();
|
||||||
|
const M = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const d = String(date.getDate()).padStart(2, '0');
|
||||||
|
const h = String(date.getHours()).padStart(2, '0');
|
||||||
|
const m = String(date.getMinutes()).padStart(2, '0');
|
||||||
|
const s = String(date.getSeconds()).padStart(2, '0');
|
||||||
|
|
||||||
|
return [
|
||||||
|
`${y}-${M}-${d} ${h}:${m}`, // 格式:YYYY-MM-DD HH:mm
|
||||||
|
`${y}-${M}-${d} ${h}:${m}:${s}`, // 格式:YYYY-MM-DD HH:mm:ss
|
||||||
|
`${y}-${M}-${d}`, // 格式:YYYY-MM-DD
|
||||||
|
`${m}`, // 分钟格式:mm
|
||||||
|
`${h}`, // 小时格式:HH
|
||||||
|
`${d}` // 日期格式:DD
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Times;
|
||||||
|
|
|
@ -0,0 +1,239 @@
|
||||||
|
const Times = require("../public/times");
|
||||||
|
// const executeWithRetry = require("./public/retry")
|
||||||
|
const Init = require("./init");
|
||||||
|
|
||||||
|
class Balance extends Init {
|
||||||
|
constructor(coin) {
|
||||||
|
const method = "balance";
|
||||||
|
super(coin, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
async query_min_height() {
|
||||||
|
try {
|
||||||
|
const sql = `select MIN(max_height) AS min_height from wallet_in where coin = ? and state = ?; `;
|
||||||
|
const result = await this.distribution.exec(sql, [this.coin, 0]);
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async query_transaction_state(height) {
|
||||||
|
try {
|
||||||
|
const sql = `SELECT * FROM sendinfo WHERE coin = ? AND height >= ?;`;
|
||||||
|
const result = await this.balancedb.exec(sql, [this.coin, height]);
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async query_now_height(last_height){
|
||||||
|
try{
|
||||||
|
const [chain_height, [db_height]] = await Promise.all([this.node.getblockcount(), this.query_min_height()])
|
||||||
|
return chain_height > db_height.min_height + this.MAX_MATURE && chain_height > last_height + 2// 在成熟高度基础上再+2高度,防止pool_account转账未更新
|
||||||
|
} catch(err){
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async update_wallet_in(ids) {
|
||||||
|
try {
|
||||||
|
const sql = `UPDATE wallet_in SET state = 1 WHERE id IN (?);`;
|
||||||
|
await this.distribution.exec_transaction(sql, [ids]);
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async insert_wallet_out_AND_update_wallet_in(data) {
|
||||||
|
try {
|
||||||
|
let sql = `INSERT INTO wallet_outv2(coin, user, address, date, max_height, tx_id, amount, tx_fee) VALUES `;
|
||||||
|
const values = [];
|
||||||
|
let id = [];
|
||||||
|
data.forEach((item) => {
|
||||||
|
const { username, qty, fee, txid, time, userid, height, ids } = item;
|
||||||
|
if (txid) {
|
||||||
|
id = id.concat(ids.split(","));
|
||||||
|
values.push(this.coin, userid, username, Times.utcTime(time * 1000), height, txid, qty, fee);
|
||||||
|
sql += `(?,?,?,?,?,?,?,?), `;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (values.length === 0) {
|
||||||
|
console.log(`${Date.now()}: ${this.coin}无新增转账`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sql = sql.slice(0, -2);
|
||||||
|
const wallet_in_sql = `UPDATE wallet_in SET state = 1 WHERE id IN (?);`;
|
||||||
|
await Promise.all([this.distribution.exec_transaction(sql, values)], this.distribution.exec_transaction(wallet_in_sql, [id]));
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async main() {
|
||||||
|
try {
|
||||||
|
const min_height = await this.query_min_height();
|
||||||
|
if (min_height.length === 0 || !min_height[0].min_height) {
|
||||||
|
console.log(`${Times.bjTime(Date.now().valueOf())}: ${this.coin}无需要更新的数据`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const need_update_data = await this.query_transaction_state(min_height[0].min_height);
|
||||||
|
if (need_update_data.length === 0) {
|
||||||
|
console.log(`${Times.bjTime(Date.now().valueOf())}: ${this.coin}钱包暂无转账信息`);
|
||||||
|
// await executeWithRetry(this.main(), 12, 60 * 5);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
await this.insert_wallet_out_AND_update_wallet_in(need_update_data);
|
||||||
|
return false
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DGBBlance extends Init {
|
||||||
|
constructor(coin) {
|
||||||
|
const method = "balance";
|
||||||
|
super(coin, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
async query_min_height() {
|
||||||
|
try {
|
||||||
|
const sql = `select MIN(max_height) AS min_height from wallet_in where coin like "dgb%" and state = ?; `;
|
||||||
|
const result = await this.distribution.exec(sql, [0]);
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async check_height() {
|
||||||
|
try {
|
||||||
|
const my_sql = `
|
||||||
|
SELECT height FROM dgbs_blkreportprofitv2 WHERE date >= "2024-11-26 00:00:00" and date < "2024-11-27 00:00:00"
|
||||||
|
UNION
|
||||||
|
SELECT height FROM dgbo_blkreportprofitv2 WHERE date >= "2024-11-26 00:00:00" and date < "2024-11-27 00:00:00"
|
||||||
|
UNION
|
||||||
|
SELECT height FROM dgbq_blkreportprofitv2 WHERE date >= "2024-11-26 00:00:00" and date < "2024-11-27 00:00:00";`;
|
||||||
|
const balance_sql = `SELECT height FROM balanceinfo WHERE height > 20402916 AND height <= 20408691;`;
|
||||||
|
const [my_data, balance_data] = await Promise.all([this.distribution.exec(my_sql), this.balancedb.exec(balance_sql)]);
|
||||||
|
const my_result = [];
|
||||||
|
const balance_result = [];
|
||||||
|
for (let item of my_data) {
|
||||||
|
my_result.push(item.height);
|
||||||
|
}
|
||||||
|
for (let item of balance_data) {
|
||||||
|
balance_result.push(item.height);
|
||||||
|
}
|
||||||
|
// 找出 array1 中不在 array2 的元素
|
||||||
|
const onlyInArray1 = my_result.filter((item) => !balance_result.includes(item));
|
||||||
|
|
||||||
|
// 找出 array2 中不在 array1 的元素
|
||||||
|
const onlyInArray2 = balance_result.filter((item) => !my_result.includes(item));
|
||||||
|
|
||||||
|
// 合并结果
|
||||||
|
const difference = [...onlyInArray1, ...onlyInArray2];
|
||||||
|
return difference;
|
||||||
|
console.log(difference); // 输出: [1, 2, 5, 6]
|
||||||
|
// for(let item of difference){
|
||||||
|
// if(!my_result.includes(item)){
|
||||||
|
// console.log(`${item}不在池子报块中`);
|
||||||
|
// } else {
|
||||||
|
// console.log(`${item}不在钱包报块中`);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async query_now_height(last_height){
|
||||||
|
try{
|
||||||
|
const sql = `SELECT MAX(max_height) AS max_height FROM wallet_in WHERE coin Like "dgb%" AND state = ?;`
|
||||||
|
const [chain_height, [db_height]] = await Promise.all([this.node.getblockcount(), this.distribution.exec(sql, [0])])
|
||||||
|
return chain_height > db_height.max_height + this.MAX_MATURE && chain_height > last_height + 2
|
||||||
|
} catch(err){
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async query_transaction_state(height) {
|
||||||
|
try {
|
||||||
|
const sql = `SELECT * FROM sendinfo WHERE coin LIKE "dgb%" AND height >= ?;`;
|
||||||
|
const result = await this.balancedb.exec(sql, [height]);
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async query_coin(id) {
|
||||||
|
try {
|
||||||
|
const sql = `SELECT coin FROM wallet_in WHERE id = ?;`;
|
||||||
|
const result = await this.distribution.exec(sql, [id]);
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async update_wallet_in(ids) {
|
||||||
|
try {
|
||||||
|
const sql = `UPDATE wallet_in SET state = 1 WHERE id IN (?);`;
|
||||||
|
await this.distribution.exec_transaction(sql, [ids]);
|
||||||
|
return;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async insert_wallet_out_AND_update_wallet_in(data) {
|
||||||
|
try {
|
||||||
|
let sql = `INSERT INTO wallet_outv2(coin, user, address, date, max_height, tx_id, amount, tx_fee) VALUES `;
|
||||||
|
const values = [];
|
||||||
|
let id = [];
|
||||||
|
data.forEach((item) => {
|
||||||
|
const { coin, username, qty, fee, txid, time, userid, height, ids } = item;
|
||||||
|
if (txid) {
|
||||||
|
id = id.concat(ids.split(","));
|
||||||
|
values.push(coin, userid, username, Times.utcTime(time * 1000), height, txid, qty, fee);
|
||||||
|
sql += `(?,?,?,?,?,?,?,?), `;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
sql = sql.slice(0, -2);
|
||||||
|
const wallet_in_sql = `UPDATE wallet_in SET state = 1 WHERE id IN (?);`;
|
||||||
|
await Promise.all([this.distribution.exec_transaction(wallet_in_sql, [id]), this.distribution.exec_transaction(sql, values)]);
|
||||||
|
return;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async main() {
|
||||||
|
try {
|
||||||
|
const min_height = await this.query_min_height();
|
||||||
|
if (min_height.length === 0 || !min_height[0].min_height) {
|
||||||
|
console.log(`${Date.now()}: dgb无需要更新的数据`);
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
const need_update_data = await this.query_transaction_state(min_height[0].min_height);
|
||||||
|
if (need_update_data.length === 0) {
|
||||||
|
console.log(`${Date.now()}: dgb转账未完成`);
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
const data = [];
|
||||||
|
for (let item of need_update_data) {
|
||||||
|
const { username, qty, fee, txid, time, userid, height, ids } = item;
|
||||||
|
const coin = await this.query_coin(ids.split(",")[0]);
|
||||||
|
data.push({ coin: coin[0].coin, username, qty, fee, txid, time, userid, height, ids });
|
||||||
|
}
|
||||||
|
await this.insert_wallet_out_AND_update_wallet_in(data);
|
||||||
|
return false
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { Balance, DGBBlance };
|
|
@ -0,0 +1,92 @@
|
||||||
|
const Times = require("../public/times");
|
||||||
|
const Init = require("./init");
|
||||||
|
|
||||||
|
class ClearDBData extends Init {
|
||||||
|
constructor(coin) {
|
||||||
|
const method = "clear"
|
||||||
|
super(coin, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除X小时前的sharesdb数据,hours必须大于24小时
|
||||||
|
* @param {Number} hours
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async clearSharesDB(hours) {
|
||||||
|
if(hours < 24){
|
||||||
|
console.log(`sharesdb最多只能删除24小时之前的数据`);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// 修正查询语句,去掉 'from' 和 'to' 字段的单引号
|
||||||
|
const query_sharesdb_table_sql = `
|
||||||
|
SELECT \`from\`, \`to\`
|
||||||
|
FROM ${this.coin}_blk_height_detail
|
||||||
|
WHERE date <= NOW() - INTERVAL ? HOUR;
|
||||||
|
`;
|
||||||
|
|
||||||
|
// 获取需要删除的高度范围
|
||||||
|
const need_del_height = await this.sharesdb.exec(query_sharesdb_table_sql, [hours]);
|
||||||
|
if (need_del_height.length === 0) {
|
||||||
|
console.log(`${Date.now()}:${this.coin}暂无需要清理的shares detail`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成删除表的 SQL 语句
|
||||||
|
let delete_sql = `DROP TABLE IF EXISTS `;
|
||||||
|
need_del_height.forEach((item) => {
|
||||||
|
const { from, to } = item;
|
||||||
|
const table = `${this.coin}_block_detail_${from}_${Number((to - 1).toFixed(0))}`;
|
||||||
|
delete_sql += `${table}, `;
|
||||||
|
});
|
||||||
|
delete_sql = delete_sql.slice(0, -2) + ";"; // 去掉最后的逗号并加上分号
|
||||||
|
// 执行删除表的 SQL 语句
|
||||||
|
await this.sharesdb.exec(delete_sql);
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除pooldb,只支持0时刻
|
||||||
|
* @param {*} start_time 开始时间 yyyy-MM-dd hh-mm-ss格式 例如"2024-11-11 00:00:00"
|
||||||
|
* @param {*} end_time 同上
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async clearPoolDB(start_time, end_time){
|
||||||
|
try{
|
||||||
|
const interval = 86400000 // 1天的毫秒数
|
||||||
|
const start_ts = new Date(start_time).valueOf()
|
||||||
|
const end_ts = new Date(end_time).valueOf()
|
||||||
|
const count = Number((end_ts - start_ts).toFixed(0)) / interval
|
||||||
|
if (!Number.isInteger(count)){
|
||||||
|
console.log(`给定的${start_time}和${end_time}有误!`);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let sql = `DROP TABLE IF EXISTS `
|
||||||
|
for(let i=0; i<count; i++){
|
||||||
|
const table_time = Times.utcTime(start_ts + i * interval).split(" ")[0].replace(/\-/g, "")
|
||||||
|
sql += `${this.coin}_miners_${table_time}, ${this.coin}_miners_stats_${table_time}, ${this.coin}_pool_blkstats_${table_time}, `
|
||||||
|
}
|
||||||
|
sql = sql.slice(0, -2) + ";"
|
||||||
|
console.log(sql);
|
||||||
|
return
|
||||||
|
} catch(err){
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除hashrate db的xx_miners表
|
||||||
|
*/
|
||||||
|
async clearHashrateDB(){
|
||||||
|
try{
|
||||||
|
const sql = `DELETE FROM ${this.coin}_minersv2 WHERE date <= NOW() - INTERVAL 25 HOUR;`
|
||||||
|
await this.hashratedb.exec_transaction(sql)
|
||||||
|
} catch(err){
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ClearDBData
|
|
@ -0,0 +1,119 @@
|
||||||
|
const Init = require("./init");
|
||||||
|
|
||||||
|
class Confirm extends Init {
|
||||||
|
constructor(coin) {
|
||||||
|
const method = "confirm";
|
||||||
|
super(coin, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
isPositiveInteger(num) {
|
||||||
|
return Number.isInteger(num) && num > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询当前所有满足查验要求的报块,即 <= 最高成熟高度 且 状态为待定(0)的报块
|
||||||
|
async query_need_update_data(mature_height) {
|
||||||
|
try {
|
||||||
|
const sql = `SELECT height FROM ${this.coin}_blkreportprofitv2 WHERE height <= ? AND state = ?;`;
|
||||||
|
const data = await this.distribution.exec(sql, [mature_height, 0]);
|
||||||
|
if (!data || data.length === 0) {
|
||||||
|
console.log(`${mature_height}高度之前暂无需要更新的报块`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return data.map(item => item.height);
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询当前最高成熟高度,即当前最新高度 - MAX_MATURE
|
||||||
|
async query_maxture_height() {
|
||||||
|
try {
|
||||||
|
const now_height = await this.node.getblockcount();
|
||||||
|
const max_height = Number(now_height) - this.MAX_MATURE;
|
||||||
|
if (!this.isPositiveInteger(max_height)) {
|
||||||
|
console.log(`当前节点最大高度为${now_height}, 当前成熟高度为${max_height}`);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return max_height;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通过报块地址,校验高度是否为本矿池报块
|
||||||
|
async verify_block(height) {
|
||||||
|
try {
|
||||||
|
return await this.node.verify_block(height, this.REPORT_ADDRESS);
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 根据校验通过和校验失败的高度更新blkreportprofit表,1为成功,2为失败
|
||||||
|
async update_blkreporprofit_state(suc_heights, err_heights) {
|
||||||
|
try {
|
||||||
|
const sql = `UPDATE ${this.coin}_blkreportprofitv2 SET state = ? WHERE height IN (?);`;
|
||||||
|
if (err_heights.length === 0 && suc_heights.length !== 0) {
|
||||||
|
// 只有 suc_heights 更新
|
||||||
|
await this.distribution.exec_transaction(sql, [1, suc_heights]);
|
||||||
|
} else if (err_heights.length !== 0 && suc_heights.length === 0) {
|
||||||
|
// 只有 err_heights 更新
|
||||||
|
await this.distribution.exec_transaction(sql, [2, err_heights]);
|
||||||
|
} else if (err_heights.length !== 0 && suc_heights.length !== 0) {
|
||||||
|
// 同时更新 suc_heights 和 err_heights
|
||||||
|
await Promise.all([
|
||||||
|
this.distribution.exec_transaction(sql, [1, suc_heights]),
|
||||||
|
this.distribution.exec_transaction(sql, [2, err_heights]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1,查验区块链最高成熟高度
|
||||||
|
* 2,查验blk表中所有<=最高成熟高度 且 状态为待定(0)的区块
|
||||||
|
* 3,遍历这些区块高度,并通过node.verify方法校验
|
||||||
|
* 4,将校验通过(正常报块)和校验不通过(孤块)分为两组
|
||||||
|
* 5,将正常报块组的状态改为1,将孤块组状态改为2
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async main() {
|
||||||
|
try {
|
||||||
|
const mature_max_height = await this.query_maxture_height();
|
||||||
|
if (!mature_max_height) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const need_update_heights = await this.query_need_update_data(mature_max_height);
|
||||||
|
if (!need_update_heights) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const suc_heights = [];
|
||||||
|
const err_heights = [];
|
||||||
|
for (let item of need_update_heights) {
|
||||||
|
const verify_result = await this.verify_block(item);
|
||||||
|
if (!verify_result) {
|
||||||
|
err_heights.push(item);
|
||||||
|
} else {
|
||||||
|
suc_heights.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(err_heights.length === 0 && need_update_heights.length !== 0){
|
||||||
|
console.log(`${mature_max_height}之前有新报块,且无孤块`);
|
||||||
|
} else if(err_heights.length !== 0 && need_update_heights.length === 0){
|
||||||
|
console.log(`${mature_max_height}之前有新报块,但这些报块都是孤块`);
|
||||||
|
} else if(err_heights.length !== 0 && need_update_heights.length !== 0){
|
||||||
|
console.log(`${mature_max_height}之前有新报块,且其中有孤块`);
|
||||||
|
}
|
||||||
|
await this.update_blkreporprofit_state(suc_heights, err_heights);
|
||||||
|
return
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Confirm
|
|
@ -0,0 +1,376 @@
|
||||||
|
const Times = require("../public/times");
|
||||||
|
const Decimal = require("decimal");
|
||||||
|
const Init = require("./init");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询规定窗口期内的总报块奖励,查询规定窗口期各个挖矿账户24小时平均算力
|
||||||
|
* 通过各个账户24小时平均算力,计算出各个账户的算力占比
|
||||||
|
* 根据算力占比对总报块奖励进行分配
|
||||||
|
* 将分配的结果写入wallet_in表
|
||||||
|
*/
|
||||||
|
class Distribution extends Init {
|
||||||
|
constructor(coin) {
|
||||||
|
const method = "distribution";
|
||||||
|
super(coin, method);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 计算每个用户最终得分,满分为100分
|
||||||
|
* @param {Array} alluser_mhs24h 每个用户过去24小时平均算力
|
||||||
|
* @param {Number} hash_percent 24小时平均算力权重
|
||||||
|
* @returns 每个用户最终得分
|
||||||
|
*/
|
||||||
|
score(alluser_mhs24h, hash_percent = 1) {
|
||||||
|
// 提取 mhs24h 数值
|
||||||
|
const hashrateValues = alluser_mhs24h.map((obj) => obj.mhs24h);
|
||||||
|
// 计算总和
|
||||||
|
const totalHashrate = hashrateValues.reduce((sum, value) => sum + value, 0);
|
||||||
|
const result = {};
|
||||||
|
// 计算每个用户的算力占比
|
||||||
|
for (let { user, mhs24h } of alluser_mhs24h) {
|
||||||
|
result[user] = (mhs24h / totalHashrate) * hash_percent;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询指定日期用户的24小时总算力
|
||||||
|
* @param {Date} date 采用yyyy-MM-dd格式(只支持0时刻),例如"2024-11-11"
|
||||||
|
*/
|
||||||
|
async query_last_day_hashrate(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 + " 00:00:00"]);
|
||||||
|
if (data.length === 0) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
const result = [];
|
||||||
|
for (let item of data) {
|
||||||
|
const { user, mhs24h } = item;
|
||||||
|
result.push({ user, mhs24h: Number(mhs24h) });
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询指定日期报块总奖励
|
||||||
|
* @param {Date} start_time 采用yyyy-MM-dd格式,例如"2024-11-11"
|
||||||
|
* @param {Date} end_time 同上
|
||||||
|
* @return {Number} 昨日总奖励
|
||||||
|
*/
|
||||||
|
async query_last_day_reward(start_time, end_time) {
|
||||||
|
try {
|
||||||
|
const sql = `SELECT MAX(height) AS max_height, SUM(reward) AS reward FROM ${this.coin}_blkreportprofitv2 WHERE date >= ? AND date < ? AND state = ?;`;
|
||||||
|
const data = await this.distributiondb.exec(sql, [start_time, end_time, 1]);
|
||||||
|
if (data.length === 0 || !data[0].reward) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
data[0].reward = Number(data[0].reward);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 校验昨天所有报块是否成熟
|
||||||
|
* @param {Date} start_time 采用yyyy-MM-dd hh:mm:ss格式,例如"2024-11-11 00:00:00"
|
||||||
|
* @param {Date} end_time 同上
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async query_last_day_if_mature(start_time, end_time) {
|
||||||
|
try {
|
||||||
|
const sql = `SELECT count(*) AS count FROM ${this.coin}_blkreportprofitv2 WHERE date >= ? AND date < ? AND state = ?;`;
|
||||||
|
let data;
|
||||||
|
|
||||||
|
// 使用循环替代递归
|
||||||
|
do {
|
||||||
|
await this.sleep(1000 * 60 * 15); // 等待 15 分钟
|
||||||
|
data = await this.distributiondb.exec(sql, [start_time, end_time, 0]);
|
||||||
|
|
||||||
|
// 动态获取当前小时
|
||||||
|
const currentHour = Number(Times.times()[4]);
|
||||||
|
if(this.coin === "rxd"){
|
||||||
|
if (currentHour >= 9) {
|
||||||
|
console.log("已超过凌晨 9 点,停止检查。");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (currentHour >= 4) {
|
||||||
|
console.log("已超过凌晨 4 点,停止检查。");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (data[0].count !== 0);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询当前挖矿账户状态,包括起付额和钱包地址
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async query_users_address() {
|
||||||
|
try {
|
||||||
|
const sql = `
|
||||||
|
SELECT
|
||||||
|
a.miner_user AS 'user',
|
||||||
|
b.balance AS 'address',
|
||||||
|
b.amount AS 'amount',
|
||||||
|
b.active AS 'state'
|
||||||
|
FROM
|
||||||
|
user_account_balance b
|
||||||
|
LEFT JOIN user_miner_account a ON b.ma_id = a.id
|
||||||
|
WHERE
|
||||||
|
a.coin = ?
|
||||||
|
AND b.status = 0;`;
|
||||||
|
const data = await this.users_addresses.exec(sql, [this.coin]);
|
||||||
|
if (!data || data.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验报块
|
||||||
|
* @param {Number} height
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async verify_block(height) {
|
||||||
|
try {
|
||||||
|
const data = await this.node.verify_block(height, this.REPORT_ADDRESS);
|
||||||
|
if (data && typeof data === "object") {
|
||||||
|
return this.node.block(data);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将漏掉的报块插入到数据库中
|
||||||
|
* @param {Array} data [{time: 1708256800, height: 123456, hash: "0x1234567890", reward: 100, fees: 10}]
|
||||||
|
*/
|
||||||
|
async insert_blkreportprofit(data) {
|
||||||
|
try {
|
||||||
|
let sql = `INSERT INTO ${this.coin}_blkreportprofitv2 (date, height, hash, reward, fees, state) VALUES `;
|
||||||
|
const values = [];
|
||||||
|
data.forEach((item) => {
|
||||||
|
const { time, height, hash, block_reward, block_fees } = item;
|
||||||
|
sql += `(?,?,?,?,?,?), `;
|
||||||
|
values.push(Times.utcTime(time * 1000), height, hash, block_reward, block_fees, 1);
|
||||||
|
});
|
||||||
|
sql = sql.slice(0, -2);
|
||||||
|
await this.distributiondb.exec_transaction(sql, values);
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验昨日是否有漏掉报块的情况
|
||||||
|
* @param {Date} date 当前时间
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async check_last_data_blk(date) {
|
||||||
|
try {
|
||||||
|
const ts = new Date(date).valueOf() - 86400000; // 前一天
|
||||||
|
const yMd = Times.utcTime(ts).split(" ")[0]; // "2024-10-11"
|
||||||
|
const ymd = yMd.split("-"); // ["2024", "10", "11"]
|
||||||
|
const table_name = `${this.coin}_pool_blkstats_${ymd[0]}${ymd[1]}${ymd[2]}`;
|
||||||
|
const confirm_if_table = `SHOW TABLES LIKE '${table_name}';`;
|
||||||
|
const confirm_result = await this.pooldb.exec(confirm_if_table);
|
||||||
|
if (!confirm_result || confirm_result.length === 0) {
|
||||||
|
console.log(`pool_blkstats表未更新,退出本次执行,请手动校验`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const heights = [];
|
||||||
|
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 pool_data = master_data.concat(slave_data);
|
||||||
|
for (let item of pool_data) {
|
||||||
|
heights.push(item.height);
|
||||||
|
}
|
||||||
|
const blkreport_heighs = [];
|
||||||
|
const query_blkreport_sql = `SELECT height FROM ${this.coin}_blkreportprofitv2 WHERE DATE(date) = ? AND state = ?;`;
|
||||||
|
const blkreport_data = await this.distributiondb.exec(query_blkreport_sql, [yMd, 1]);
|
||||||
|
for (let item of blkreport_data) {
|
||||||
|
blkreport_heighs.push(item.height);
|
||||||
|
}
|
||||||
|
const setB = new Set(blkreport_heighs);
|
||||||
|
const need_check_heights = heights.filter((item) => !setB.has(item));
|
||||||
|
|
||||||
|
if (need_check_heights.length === 0) {
|
||||||
|
console.log(`${this.coin}check 完成,没有需要重新校验的区块`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const need_insert_data = [];
|
||||||
|
for (let height of need_check_heights) {
|
||||||
|
const result = await this.verify_block(height);
|
||||||
|
if (result) {
|
||||||
|
need_insert_data.push(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (need_insert_data.length === 0) {
|
||||||
|
console.log(`${this.coin}check 完成,没有需要insert的区块`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
await this.insert_blkreportprofit(need_insert_data);
|
||||||
|
console.log(`${this.coin}check 完成,已将${this.coin}漏掉的报块全部插入blk表中!`);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新wallet_in表中不满足起付额的用户状态
|
||||||
|
* @param {Array} min_amount [{"user": amount}]
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async update_state(min_amount) {
|
||||||
|
try {
|
||||||
|
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]); // []
|
||||||
|
if (!data || data.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const need_update_state = [];
|
||||||
|
for (let item of data) {
|
||||||
|
if (item.profit >= min_amount[item.user]) {
|
||||||
|
need_update_state.push(item.user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (need_update_state.length === 0) {
|
||||||
|
console.log(`${this.coin}无需要更新状态的用户(2->0)`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const values = [];
|
||||||
|
let update_sql = `UPDATE wallet_in SET state = ? WHERE coin = ? AND user = ?;`;
|
||||||
|
need_update_state.forEach(async (item) => {
|
||||||
|
const { user } = item;
|
||||||
|
update_sql += `(?, ?, ?), `;
|
||||||
|
values.push(0, this.coin, user);
|
||||||
|
});
|
||||||
|
update_sql = update_sql.slice(0, -2);
|
||||||
|
await this.distributiondb.exec_transaction(update_sql, values);
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将最终分配数据插入wallet_in表中
|
||||||
|
* @param {Array} data [{coin, user, address, create_date, should_out_date, max_height, amount, state}]
|
||||||
|
*/
|
||||||
|
async insert_wallet_in(data) {
|
||||||
|
try {
|
||||||
|
let sql = `INSERT INTO wallet_in(coin, user, address, create_date, should_out_date, max_height, amount, state) VALUES `;
|
||||||
|
const values = [];
|
||||||
|
data.forEach((item) => {
|
||||||
|
const { coin, user, address, create_date, should_out_date, max_height, amount, state } = item;
|
||||||
|
values.push(coin, user, address, create_date, should_out_date, max_height, amount, state);
|
||||||
|
sql += `(?,?,?,?,?,?,?,?), `;
|
||||||
|
});
|
||||||
|
sql = sql.slice(0, -2);
|
||||||
|
await this.distributiondb.exec_transaction(sql, values);
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async main(start_time, end_time) {
|
||||||
|
try {
|
||||||
|
const _if = await this.query_last_day_if_mature(start_time, end_time);
|
||||||
|
if (!_if) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const check_result = await this.check_last_data_blk(end_time)
|
||||||
|
if (!check_result) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const [last_day_mhs24h, last_day_reward, users_address] = await Promise.all([this.query_last_day_hashrate(end_time), this.query_last_day_reward(start_time, end_time), this.query_users_address()]);
|
||||||
|
if (!last_day_mhs24h || !last_day_reward || !users_address || !check_result) {
|
||||||
|
console.log(`查询错误`);
|
||||||
|
console.log("last_day_mhs24h:", last_day_mhs24h);
|
||||||
|
console.log("last_day_reward:", last_day_reward);
|
||||||
|
console.log("users_address:", users_address);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const min_amount = {};
|
||||||
|
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;
|
||||||
|
let should_out_date; // 实际转账时间
|
||||||
|
let accuracy; // user保留小数位数,100为2位,以此类推
|
||||||
|
let count // pool_account 保留小数位数
|
||||||
|
if (this.coin === "nexa") {
|
||||||
|
should_out_date = Times.utcTime(new Date(end_time).valueOf() + 1000 * 60 * 60 * 24 * 7);
|
||||||
|
accuracy = 100;
|
||||||
|
count = 2
|
||||||
|
} else if (this.coin === "rxd") {
|
||||||
|
accuracy = 100;
|
||||||
|
should_out_date = end_time;
|
||||||
|
count = 2
|
||||||
|
} else {
|
||||||
|
should_out_date = end_time;
|
||||||
|
accuracy = 100000000;
|
||||||
|
count = 8
|
||||||
|
}
|
||||||
|
let user_profit = 0;
|
||||||
|
const result = [];
|
||||||
|
let pool_account_address;
|
||||||
|
for (let user in score_ratio) {
|
||||||
|
const profit = Math.floor(score_ratio[user] * reward * accuracy) / accuracy;
|
||||||
|
if(profit === 0){
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
user_profit += profit;
|
||||||
|
for (let item of users_address) {
|
||||||
|
if (item.user === "pool_account") {
|
||||||
|
pool_account_address = item.address;
|
||||||
|
}
|
||||||
|
if (user === item.user) {
|
||||||
|
min_amount[item.user] = item.min_amount; // 今天有变动的账户才会更新最小提现金额
|
||||||
|
let state;
|
||||||
|
if (profit >= item.amount && item.state === 0) {
|
||||||
|
// 账号激活自动提现且当天收益满足起付额,state用0
|
||||||
|
state = 0;
|
||||||
|
} else if (profit < item.amount && item.state === 0) {
|
||||||
|
// 账号激活自动提现但当天收益不满足起付额,state用2
|
||||||
|
state = 2;
|
||||||
|
} else if (profit >= item.amount && item.state === 1) {
|
||||||
|
// 账号未激活自动提现但当天收益满足起付额,state用3
|
||||||
|
state = 3;
|
||||||
|
} else {
|
||||||
|
// 账号未激活自动提现且当天收益不满足起付额,state用4
|
||||||
|
state = 4;
|
||||||
|
}
|
||||||
|
result.push({ coin: this.coin, user, address: item.address, create_date: end_time, should_out_date, max_height, amount: profit, state });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const num1 = new Decimal(last_day_reward[0].reward);
|
||||||
|
const num2 = new Decimal(user_profit);
|
||||||
|
const pool_account_amount = num1.sub(num2).toString();
|
||||||
|
result.push({ coin: this.coin, user: "pool_account", address: pool_account_address, create_date: end_time, should_out_date, max_height, amount: Number(Number(pool_account_amount).toFixed(count)), state: 0 });
|
||||||
|
console.log(result);
|
||||||
|
await this.insert_wallet_in(result);
|
||||||
|
await this.update_state(min_amount);
|
||||||
|
return;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Distribution;
|
|
@ -0,0 +1,366 @@
|
||||||
|
const Times = require("../public/times");
|
||||||
|
// const executeWithRetry = require("./public/retry")
|
||||||
|
const Init = require("./init");
|
||||||
|
|
||||||
|
class HashRate extends Init {
|
||||||
|
constructor(coin) {
|
||||||
|
const method = "hashrate";
|
||||||
|
super(coin, method);
|
||||||
|
this.diffOneShareHashsAvg = 2 ** 32 - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算hash
|
||||||
|
* @param {Number} accepts 时段内接受总数
|
||||||
|
* @param {Number} seconds 时段秒数
|
||||||
|
* @param {String} unit H/s、KH/s、MH/s、GH/s、TH/s、PH/s、EH/s
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
calculate_hashrate(accepts, seconds, unit) {
|
||||||
|
let num;
|
||||||
|
switch (unit) {
|
||||||
|
case "H/s":
|
||||||
|
num = 1;
|
||||||
|
break;
|
||||||
|
case "KH/s":
|
||||||
|
num = 1_000;
|
||||||
|
break;
|
||||||
|
case "MH/s":
|
||||||
|
num = 1_000_000;
|
||||||
|
break;
|
||||||
|
case "GH/s":
|
||||||
|
num = 1_000_000_000;
|
||||||
|
break;
|
||||||
|
case "TH/s":
|
||||||
|
num = 1_000_000_000_000;
|
||||||
|
break;
|
||||||
|
case "PH/s":
|
||||||
|
num = 1_000_000_000_000_000;
|
||||||
|
break;
|
||||||
|
case "EH/s":
|
||||||
|
num = 10 ** 18;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw `${unit}不是已知单位`;
|
||||||
|
}
|
||||||
|
const hashrate = (accepts * this.diffOneShareHashsAvg) / seconds / num;
|
||||||
|
if(this.coin === "alph"){
|
||||||
|
return hashrate * 4
|
||||||
|
} else {
|
||||||
|
return hashrate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将主、备查询出来的数据合并
|
||||||
|
* @param {*} data [{user:"", miner:"", accepts:100},{user:"", miner:"", accepts:100}...]
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
merge(data) {
|
||||||
|
// 创建一个 Map 来存储 user 和 miner 组合的结果
|
||||||
|
const results = new Map();
|
||||||
|
|
||||||
|
data.forEach((item) => {
|
||||||
|
const key = `${item.user}-${item.miner}`;
|
||||||
|
|
||||||
|
if (results.has(key)) {
|
||||||
|
const existing = results.get(key);
|
||||||
|
existing.accepts += parseFloat(item.accepts);
|
||||||
|
if (new Date(item.last_submit) > new Date(existing.last_submit)) {
|
||||||
|
existing.last_submit = item.last_submit;
|
||||||
|
}
|
||||||
|
results.set(key, existing);
|
||||||
|
} else {
|
||||||
|
results.set(key, {
|
||||||
|
user: item.user,
|
||||||
|
miner: item.miner,
|
||||||
|
accepts: parseFloat(item.accepts),
|
||||||
|
last_submit: item.last_submit,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 将结果转换为数组
|
||||||
|
const resultArray = Array.from(results.values());
|
||||||
|
return resultArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询主库符合时段的表名
|
||||||
|
* @param {String} start_time
|
||||||
|
* @param {String} end_time
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async query_table(start_time, end_time) {
|
||||||
|
try {
|
||||||
|
const sql = `(SELECT date, \`from\`, \`to\` FROM ${this.coin}_blk_height_detail WHERE date >= ? ORDER BY date LIMIT 1) UNION (SELECT date, \`from\`, \`to\` FROM ${this.coin}_blk_height_detail WHERE date >= ? AND date < ?) ORDER BY date;`;
|
||||||
|
const data = await this.sharesdb.exec(sql, [end_time, start_time, end_time])
|
||||||
|
const result = [];
|
||||||
|
if (data.length !== 0) {
|
||||||
|
for (let item of data) {
|
||||||
|
result.push(`${this.coin}_block_detail_${item.from}_${Math.trunc(item.to - 1)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push(`${this.coin}_blk_detail`);
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询从库符合时段的表名
|
||||||
|
* @param {String} start_time
|
||||||
|
* @param {String} end_time
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async query_slave_table(start_time, end_time) {
|
||||||
|
try {
|
||||||
|
const sql = `(SELECT date, \`from\`, \`to\` FROM ${this.coin}_blk_height_detail WHERE date >= ? ORDER BY date LIMIT 1) UNION (SELECT date, \`from\`, \`to\` FROM ${this.coin}_blk_height_detail WHERE date >= ? AND date < ?) ORDER BY date;`;
|
||||||
|
const data = await this.sharesdb_slave.exec(sql, [end_time, start_time, end_time])
|
||||||
|
const result = [];
|
||||||
|
if (data.length !== 0) {
|
||||||
|
for (let item of data) {
|
||||||
|
result.push(`${this.coin}_block_detail_${item.from}_${Math.trunc(item.to - 1)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push(`${this.coin}_blk_detail`);
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询时段内accepts,主从同时查询
|
||||||
|
async query_accepts(start_time, end_time, enable) {
|
||||||
|
try {
|
||||||
|
if (this.count === undefined) this.count = 0;
|
||||||
|
if (enable) {
|
||||||
|
const [tables_name, slave_tables_name] = await Promise.all([this.query_table(start_time, end_time), this.query_slave_table(start_time, end_time)]);
|
||||||
|
|
||||||
|
// 查询主库符合条件的数据
|
||||||
|
let sql = ``;
|
||||||
|
if (tables_name.length <= 1) {
|
||||||
|
sql = `SELECT MAX(date) AS last_submit, user, miner, SUM(miner_diff) AS accepts FROM ${this.coin}_blk_detail WHERE date >= "${start_time}" AND date < "${end_time}" GROUP BY user, miner;`;
|
||||||
|
} else {
|
||||||
|
sql = `SELECT MAX(date) AS last_submit, user, miner, SUM(miner_diff) AS accepts FROM ( `;
|
||||||
|
for (let i = 0; i < tables_name.length; i++) {
|
||||||
|
if (i < tables_name.length - 1) {
|
||||||
|
sql += `SELECT date, user, miner, miner_diff, pool_diff FROM ${tables_name[i]} WHERE date >= "${start_time}" AND date < "${end_time}" \nUNION ALL\n`;
|
||||||
|
} else {
|
||||||
|
sql += `SELECT date, user, miner, miner_diff, pool_diff FROM ${tables_name[i]} WHERE date >= "${start_time}" AND date < "${end_time}") AS combined_tables GROUP BY user, miner;`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let slave_sql = ``;
|
||||||
|
if (slave_tables_name.length <= 1) {
|
||||||
|
slave_sql = `SELECT MAX(date) AS last_submit, user, miner, SUM(miner_diff) AS accepts FROM ${this.coin}_blk_detail WHERE date >= "${start_time}" AND date < "${end_time}" GROUP BY user, miner;`;
|
||||||
|
} else {
|
||||||
|
slave_sql = `SELECT MAX(date) AS last_submit, user, miner, SUM(miner_diff) AS accepts FROM ( `;
|
||||||
|
for (let i = 0; i < slave_tables_name.length; i++) {
|
||||||
|
if (i < slave_tables_name.length - 1) {
|
||||||
|
slave_sql += `SELECT date, user, miner, miner_diff, pool_diff FROM ${slave_tables_name[i]} WHERE date >= "${start_time}" AND date < "${end_time}" \nUNION ALL\n`;
|
||||||
|
} else {
|
||||||
|
slave_sql += `SELECT date, user, miner, miner_diff, pool_diff FROM ${slave_tables_name[i]} WHERE date >= "${start_time}" AND date < "${end_time}") AS combined_tables GROUP BY user, miner;`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// // 执行查询,并将结果合并
|
||||||
|
const [accepts_data, slave_accepts] = await Promise.all([this.sharesdb.exec(sql), this.sharesdb_slave.exec(slave_sql)]);
|
||||||
|
const accepts = this.merge(accepts_data.concat(slave_accepts)); // 合并主备accepts
|
||||||
|
return accepts;
|
||||||
|
} else {
|
||||||
|
const tables_name = await this.query_table(start_time, end_time);
|
||||||
|
let sql = ``;
|
||||||
|
if (tables_name.length <= 1) {
|
||||||
|
sql = `SELECT MAX(date) AS last_submit, user, miner, SUM(miner_diff) AS accepts FROM ${this.coin}_blk_detail WHERE date >= "${start_time}" AND date < "${end_time}" GROUP BY user, miner;`;
|
||||||
|
} else {
|
||||||
|
sql = `SELECT MAX(date) AS last_submit, user, miner, SUM(miner_diff) AS accepts FROM ( `;
|
||||||
|
for (let i = 0; i < tables_name.length; i++) {
|
||||||
|
if (i < tables_name.length - 1) {
|
||||||
|
sql += `SELECT date, user, miner, miner_diff, pool_diff FROM ${tables_name[i]} WHERE date >= "${start_time}" AND date < "${end_time}" \nUNION ALL\n`;
|
||||||
|
} else {
|
||||||
|
sql += `SELECT date, user, miner, miner_diff, pool_diff FROM ${tables_name[i]} WHERE date >= "${start_time}" AND date < "${end_time}") AS combined_tables GROUP BY user, miner;`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const accepts_data = await this.sharesdb.exec(sql);
|
||||||
|
const slave_accepts = [];
|
||||||
|
const accepts = this.merge(accepts_data.concat(slave_accepts)); // 合并主备accepts
|
||||||
|
return accepts;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error in query_accepts: ${err.message}`);
|
||||||
|
await this.sleep(1000 * 15);
|
||||||
|
if (this.count > 3) { // 重试4次,1分钟
|
||||||
|
this.count = 0;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
this.count++;
|
||||||
|
return this.query_accepts(start_time, end_time, enable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询当天miners状态,排除掉超过1天没有提交的矿工
|
||||||
|
async query_miners(time) {
|
||||||
|
try {
|
||||||
|
const sql = `SELECT date, user, miner, state, ratio, last_submit FROM ${this.coin}_miners WHERE last_submit >= DATE(?) - INTERVAL 1 DAY;`;
|
||||||
|
const miners_state = await this.pooldb.exec(sql, [time]);
|
||||||
|
return miners_state;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async insert_mhs(data) {
|
||||||
|
if (data.length === 0 || !data || data.size === 0) {
|
||||||
|
console.log(Date.now(), ":30分钟没有新增矿机提交数据");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
let sql = `INSERT INTO ${this.coin}_mhsv2 (user, miner, date, mhs30m, mhs24h, state, last_submit) VALUES `;
|
||||||
|
const values = [];
|
||||||
|
data.forEach((item) => {
|
||||||
|
const { user, miner, date, mhs30m, mhs24h, state, last_submit } = item;
|
||||||
|
sql += `(?, ?, ?, ?, ?, ?, ?), `;
|
||||||
|
values.push(user, miner, date, mhs30m, mhs24h, state, last_submit);
|
||||||
|
});
|
||||||
|
sql = sql.slice(0, -2);
|
||||||
|
await this.hashratedb.exec_transaction(sql, values);
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async insert_mhs_real(data) {
|
||||||
|
if (data.length === 0 || !data || data.size === 0) {
|
||||||
|
console.log(Date.now(), ":5分钟没有新增矿机提交数据");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const del_sql = `DELETE FROM ${this.coin}_mhs_realv2 WHERE id > 0;`;
|
||||||
|
let sql = `INSERT INTO ${this.coin}_mhs_realv2 (user, miner, date, mhs30m, mhs24h, state, last_submit) VALUES `;
|
||||||
|
const values = [];
|
||||||
|
data.forEach((item) => {
|
||||||
|
const { user, miner, date, mhs30m, mhs24h, state, last_submit } = item;
|
||||||
|
sql += `(?, ?, ?, ?, ?, ? ,?), `;
|
||||||
|
values.push(user, miner, date, mhs30m, mhs24h, state, last_submit);
|
||||||
|
});
|
||||||
|
sql = sql.slice(0, -2);
|
||||||
|
// sql += ` AS new_values ON DUPLICATE KEY UPDATE date = new_values.date, mhs30m = new_values.mhs30m, mhs24h = new_values.mhs24h, state = new_values.state, last_submit = new_values.last_submit;`;
|
||||||
|
const sqls = [{ sql: del_sql }, { sql, param: values }];
|
||||||
|
await this.hashratedb.exec_transaction_together(sqls);
|
||||||
|
} catch (err) {
|
||||||
|
// 处理错误
|
||||||
|
console.error("Transaction failed: ", err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async query_hashrate_miners_accepts(end_time) {
|
||||||
|
try {
|
||||||
|
const ymd_last_30m = Times.utcTime(new Date(end_time).valueOf() - 1000 * 60 * 30);
|
||||||
|
const ymd_last_24h = Times.utcTime(new Date(end_time).valueOf() - 1000 * 60 * 60 * 24);
|
||||||
|
const state_sql = `SELECT t1.*
|
||||||
|
FROM ${this.coin}_minersv2 t1
|
||||||
|
INNER JOIN (
|
||||||
|
SELECT user, miner, MAX(date) AS max_date
|
||||||
|
FROM ${this.coin}_minersv2
|
||||||
|
WHERE date <= ?
|
||||||
|
GROUP BY user, miner
|
||||||
|
) t2
|
||||||
|
ON t1.user = t2.user AND t1.miner = t2.miner AND t1.date = t2.max_date;`;
|
||||||
|
const mhs30m_sql = `SELECT SUM(accepts) AS accepts_30min, user, miner FROM ${this.coin}_minersv2 WHERE date >= ? AND date < ? GROUP BY user, miner;`;
|
||||||
|
const mhs24h_sql = `SELECT SUM(accepts) AS accepts_24h, user, miner FROM ${this.coin}_minersv2 WHERE date >= ? AND date < ? GROUP BY user, miner;`;
|
||||||
|
const [state, mhs30m, mhs24h] = await Promise.all([this.hashratedb.exec(state_sql, [end_time]), this.hashratedb.exec(mhs30m_sql, [ymd_last_30m, end_time]), this.hashratedb.exec(mhs24h_sql, [ymd_last_24h, end_time])]);
|
||||||
|
|
||||||
|
const hashrate_map = new Map();
|
||||||
|
|
||||||
|
state.forEach((item) => {
|
||||||
|
const { date, user, miner, state, last_submit } = item;
|
||||||
|
hashrate_map.set(`${user}:${miner}`, { date: end_time, user, miner, state, last_submit, mhs30m: 0, mhs24h: 0 });
|
||||||
|
});
|
||||||
|
|
||||||
|
mhs30m.forEach((item) => {
|
||||||
|
const { accepts_30min, user, miner } = item;
|
||||||
|
|
||||||
|
const values = hashrate_map.get(`${user}:${miner}`);
|
||||||
|
|
||||||
|
values.mhs30m = this.calculate_hashrate(accepts_30min, 60 * 30, "MH/s");
|
||||||
|
|
||||||
|
hashrate_map.set(`${user}:${miner}`, values);
|
||||||
|
});
|
||||||
|
mhs24h.forEach((item) => {
|
||||||
|
const { accepts_24h, user, miner } = item;
|
||||||
|
const values = hashrate_map.get(`${user}:${miner}`);
|
||||||
|
|
||||||
|
values.mhs24h = this.calculate_hashrate(accepts_24h, 60 * 60 * 24, "MH/s");
|
||||||
|
|
||||||
|
hashrate_map.set(`${user}:${miner}`, values);
|
||||||
|
});
|
||||||
|
return hashrate_map;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async insert_hashrate_miners_table(end_time) {
|
||||||
|
try {
|
||||||
|
const ymd = end_time.split(":");
|
||||||
|
const date = ymd[0] + ":" + ymd[1] + ":" + "00";
|
||||||
|
// 计算最近5分钟accepts,最新矿机状态
|
||||||
|
const start_time = Times.utcTime(new Date(end_time).valueOf() - 1000 * 60 * 5);
|
||||||
|
let enable = (await this.redis.get(`${this.coin}:enable`)) || false;
|
||||||
|
|
||||||
|
let [accepts, miners_state] = await Promise.all([this.query_accepts(start_time, end_time, enable), this.query_miners(end_time)]);
|
||||||
|
|
||||||
|
// 创建nexa_miners表所需要的map
|
||||||
|
const miners_map = new Map();
|
||||||
|
// 判断各种情况
|
||||||
|
if (accepts.length === 0 && miners_state.length === 0) {
|
||||||
|
// 历史上没有矿工接入
|
||||||
|
return;
|
||||||
|
} else if (accepts.length !== 0 && miners_state.length === 0) {
|
||||||
|
// 主库出了问题,基本不可能出现这种情况
|
||||||
|
return;
|
||||||
|
} else if (accepts.length === 0 && miners_state.length !== 0) {
|
||||||
|
// 最近5分钟没有矿工接入,直接将m2pooldb-nexa_miners表中所有矿工的accepts更新为0,并放入nexa_miners表需要的map中
|
||||||
|
miners_state.forEach((item) => {
|
||||||
|
const { user, miner, state, last_submit } = item;
|
||||||
|
miners_map.set(`${user}:${miner}`, { date, user, miner, state: "offline", last_submit, accepts: 0 });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 先找到所有最近5分钟有提交的矿机
|
||||||
|
accepts.forEach((item) => {
|
||||||
|
const { user, miner, accepts, last_submit } = item;
|
||||||
|
miners_map.set(`${user}:${miner}`, { date, user, miner, accepts, last_submit, state: "online" });
|
||||||
|
});
|
||||||
|
// 再将stats表有记录矿机,但最近5分钟没有提交的矿机合并进去
|
||||||
|
miners_state.forEach((item) => {
|
||||||
|
const { user, miner, state, last_submit } = item;
|
||||||
|
if (!miners_map.get(`${user}:${miner}`)) {
|
||||||
|
miners_map.set(`${user}:${miner}`, { date, user, miner, accepts: 0, last_submit, state });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 将指定时段内的数据插入nexa_miners表
|
||||||
|
let insert_miners_table_sql = `INSERT INTO ${this.coin}_minersv2(user, miner, date, accepts, state, last_submit) VALUES `;
|
||||||
|
const miners_table_values = [];
|
||||||
|
miners_map.forEach((item) => {
|
||||||
|
const { user, miner, date, accepts, state, last_submit } = item;
|
||||||
|
insert_miners_table_sql += `(?, ?, ?, ?, ?, ?), `;
|
||||||
|
miners_table_values.push(user, miner, date, accepts, state, last_submit);
|
||||||
|
});
|
||||||
|
insert_miners_table_sql = insert_miners_table_sql.slice(0, -2);
|
||||||
|
await this.hashratedb.exec_transaction(insert_miners_table_sql, miners_table_values);
|
||||||
|
return;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = HashRate
|
|
@ -0,0 +1,96 @@
|
||||||
|
const fs = require("fs")
|
||||||
|
const DBPool = require("../lib/mysql");
|
||||||
|
const Cache = require("../lib/redis");
|
||||||
|
const { NEXARPCNode, GRSRPCNode, MONARPCNode, DGBRPCNode, RXDRPCNode, ENXNode, ALPHRPCNode } = require("../lib/node");
|
||||||
|
|
||||||
|
class Init{
|
||||||
|
constructor(coin, method){
|
||||||
|
this.coin = coin
|
||||||
|
const config = fs.readFileSync(`./config/${coin}.conf`, "utf-8")
|
||||||
|
const {master, slave, redis_options, node_options, distribution_conf, MAX_MATURE, REPORT_ADDRESS} = JSON.parse(config)
|
||||||
|
const {pooldb, sharesdb, distribution, hashrate, users_addresses, balance} = master
|
||||||
|
const {pooldb_slave, sharesdb_slave} = slave
|
||||||
|
const {node1, node2} = node_options
|
||||||
|
const {redis1} = redis_options
|
||||||
|
const {POOL_FEE} = distribution_conf
|
||||||
|
const node_map = {
|
||||||
|
mona: MONARPCNode,
|
||||||
|
nexa: NEXARPCNode,
|
||||||
|
grs: GRSRPCNode,
|
||||||
|
dgbs: DGBRPCNode,
|
||||||
|
dgbq: DGBRPCNode,
|
||||||
|
dgbo: DGBRPCNode,
|
||||||
|
rxd: RXDRPCNode,
|
||||||
|
enx: ENXNode,
|
||||||
|
alph: ALPHRPCNode
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (method){
|
||||||
|
case "hashrate":
|
||||||
|
this.sharesdb = new DBPool(coin, sharesdb)
|
||||||
|
this.sharesdb_slave = new DBPool(coin, sharesdb_slave)
|
||||||
|
this.pooldb = new DBPool(coin, pooldb)
|
||||||
|
this.redis = new Cache(redis1)
|
||||||
|
// this.pooldb_slave = new DBPool(coin, pooldb_slave)
|
||||||
|
this.hashratedb = new DBPool(coin, hashrate)
|
||||||
|
break
|
||||||
|
case "report":
|
||||||
|
if(this.coin === "enx"){
|
||||||
|
this.distribution = new DBPool(coin, distribution)
|
||||||
|
this.pooldb = new DBPool(coin, pooldb)
|
||||||
|
} else {
|
||||||
|
this.REPORT_ADDRESS = REPORT_ADDRESS
|
||||||
|
this.node = new node_map[coin](node2)
|
||||||
|
this.distribution = new DBPool(coin, distribution)
|
||||||
|
this.redis = new Cache(redis1)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case "clear":
|
||||||
|
this.pooldb = new DBPool(coin, pooldb)
|
||||||
|
this.sharesdb = new DBPool(coin, sharesdb)
|
||||||
|
this.hashratedb = new DBPool(coin, hashrate)
|
||||||
|
break
|
||||||
|
case "distribution":
|
||||||
|
this.pooldb = new DBPool(coin, pooldb)
|
||||||
|
this.pooldb_slave = new DBPool(coin, pooldb_slave)
|
||||||
|
this.hashratedb = new DBPool(coin, hashrate)
|
||||||
|
this.distributiondb = new DBPool(coin, distribution)
|
||||||
|
this.users_addresses = new DBPool(coin, users_addresses)
|
||||||
|
this.node = new node_map[coin](node2)
|
||||||
|
this.REPORT_ADDRESS = REPORT_ADDRESS
|
||||||
|
this.POOL_FEE = POOL_FEE
|
||||||
|
console.log(`当前手续费率为:${POOL_FEE}`);
|
||||||
|
// this.balance = new DBPool(coin, balance)
|
||||||
|
break
|
||||||
|
case "balance":
|
||||||
|
this.distribution = new DBPool(coin, distribution)
|
||||||
|
this.balancedb = new DBPool(coin, balance)
|
||||||
|
this.node = new node_map[coin](node2)
|
||||||
|
this.MAX_MATURE = MAX_MATURE
|
||||||
|
break
|
||||||
|
case "confirm":
|
||||||
|
this.MAX_MATURE = MAX_MATURE
|
||||||
|
this.REPORT_ADDRESS = REPORT_ADDRESS
|
||||||
|
this.node = new node_map[coin](node2)
|
||||||
|
this.distribution = new DBPool(coin, distribution)
|
||||||
|
this.pooldb = new DBPool(coin, pooldb)
|
||||||
|
break
|
||||||
|
case "stats":
|
||||||
|
this.pooldb = new DBPool(coin, pooldb)
|
||||||
|
this.hashratedb = new DBPool(coin, hashrate)
|
||||||
|
this.distribution = new DBPool(coin, distribution)
|
||||||
|
break
|
||||||
|
case "notice":
|
||||||
|
this.distribution = new DBPool(coin, distribution)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
throw `暂不支持${method}方法 init`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(ms) {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Init
|
|
@ -0,0 +1,85 @@
|
||||||
|
const Init = require("./init");
|
||||||
|
const nodemailer = require("nodemailer");
|
||||||
|
const Times = require("../public/times");
|
||||||
|
|
||||||
|
const conf = {
|
||||||
|
create_config: {
|
||||||
|
service: "qq",
|
||||||
|
secureConnection: true,
|
||||||
|
port: 465,
|
||||||
|
auth: {
|
||||||
|
user: "393768033@qq.com",
|
||||||
|
// pass: "hyaefuvaiudcbihj",
|
||||||
|
pass:"anuflpiziamwcaia",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
symbol:["nexa", "rxd", "dgbs", "dgbq", "dgbo", "grs", "mona", "enx"],
|
||||||
|
receiver: "709387953@qq.com, liarsars@gmail.com"
|
||||||
|
};
|
||||||
|
|
||||||
|
class Notice extends Init {
|
||||||
|
constructor(coin) {
|
||||||
|
super(coin, "notice");
|
||||||
|
this.conf = conf;
|
||||||
|
this.transporter = nodemailer.createTransport(conf.create_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
construct_mail(receiver, subject, text, html) {
|
||||||
|
return {
|
||||||
|
from: this.transporter.options.auth.user,
|
||||||
|
to: receiver,
|
||||||
|
subject: subject,
|
||||||
|
text: text,
|
||||||
|
html: html
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async send_mail(mailOptions) {
|
||||||
|
if (!mailOptions.to) {
|
||||||
|
throw new Error("未指定收件人");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const info = await this.transporter.sendMail(mailOptions);
|
||||||
|
console.log(`${Date.now()} 检测到未转账信息!邮件ID: ${info.messageId}`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("邮件发送失败:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async query_walletout(date) {
|
||||||
|
try {
|
||||||
|
const sql = `SELECT * FROM wallet_outv2 WHERE DATE(date) = ?;`;
|
||||||
|
const data = await this.distribution.exec(sql, [date]);
|
||||||
|
return data.length > 0 ? data : false;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async main() {
|
||||||
|
try {
|
||||||
|
const date = Times.times()[2];
|
||||||
|
const data = await this.query_walletout(date);
|
||||||
|
|
||||||
|
if (!data) return;
|
||||||
|
|
||||||
|
const symbol = new Set(data.map(item => item.coin));
|
||||||
|
|
||||||
|
const transfer_err_coins = (this.conf.symbol || []).filter(item => !symbol.has(item));
|
||||||
|
|
||||||
|
if (transfer_err_coins.length === 0) return;
|
||||||
|
|
||||||
|
const subject = `今日有未转账币种`;
|
||||||
|
const text = `今日有未转账币种`;
|
||||||
|
const html = `今日未转账币种:${transfer_err_coins.join(", ")}<h1>测试数据</h1>`;
|
||||||
|
|
||||||
|
const mailOptions = this.construct_mail(this.conf.receiver, subject, text, html);
|
||||||
|
await this.send_mail(mailOptions);
|
||||||
|
return
|
||||||
|
} catch (err) {
|
||||||
|
console.error("主任务执行错误:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Notice
|
|
@ -0,0 +1,182 @@
|
||||||
|
const Times = require("../public/times");
|
||||||
|
const Init = require("./init");
|
||||||
|
|
||||||
|
class Report extends Init {
|
||||||
|
constructor(coin) {
|
||||||
|
const method = "report";
|
||||||
|
super(coin, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
async query_mysql_last_height() {
|
||||||
|
try {
|
||||||
|
const sql = `SELECT MAX(height) AS max_height FROM ${this.coin}_blkreportprofitv2;`;
|
||||||
|
const data = await this.distribution.exec(sql);
|
||||||
|
if (!data || data.length === 0) {
|
||||||
|
throw `${this.coin}当前无报块记录`;
|
||||||
|
}
|
||||||
|
return data[0].max_height;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async query_redis_last_height() {
|
||||||
|
try {
|
||||||
|
const data = await this.redis.get(`${this.coin}:last_check`);
|
||||||
|
if (!data) {
|
||||||
|
const result = await this.query_mysql_last_height();
|
||||||
|
await this.redis.set(`${this.coin}:last_check`, result);
|
||||||
|
console.log(`redis中无${this.coin} last_check数据,采用最后一个报块高度!`);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return Number(data);
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async query_chain_last_height() {
|
||||||
|
try {
|
||||||
|
const data = await this.node.getblockcount();
|
||||||
|
return Number(data);
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async insert_blkreportprofit(data) {
|
||||||
|
try {
|
||||||
|
let sql = `INSERT INTO ${this.coin}_blkreportprofitv2 (date, height, hash, reward, fees, state) VALUES `;
|
||||||
|
const values = [];
|
||||||
|
for (let item of data) {
|
||||||
|
const { date, height, hash, reward, fees } = item;
|
||||||
|
values.push(date, height, hash, reward, fees, 0);
|
||||||
|
sql += `(?,?,?,?,?,?), `;
|
||||||
|
}
|
||||||
|
sql = sql.slice(0, -2);
|
||||||
|
await this.distribution.exec_transaction(sql, values);
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async main() {
|
||||||
|
try {
|
||||||
|
const [redis_height, chain_height] = await Promise.all([this.query_redis_last_height(), this.query_chain_last_height()]);
|
||||||
|
// 区块链高度小于last_check高度,节点同步出错
|
||||||
|
if (chain_height < redis_height) {
|
||||||
|
console.log(`${this.coin}节点同步出错,节点高度${chain_height},last_check${redis_height}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (chain_height === redis_height) {
|
||||||
|
console.log(`${this.coin}当前节点和last_check高度一致,无需校验`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const suc_data = [];
|
||||||
|
for (let i = redis_height + 1; i <= chain_height; i++) {
|
||||||
|
const check_result = await this.node.verify_block(i, this.REPORT_ADDRESS);
|
||||||
|
if (check_result) {
|
||||||
|
const block = this.node.block(check_result);
|
||||||
|
const { height, hash, time, block_reward, block_fees } = block;
|
||||||
|
suc_data.push({ date: Times.utcTime(time * 1000), height, hash, reward: block_reward, fees: block_fees });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (suc_data.length === 0) {
|
||||||
|
console.log(`${redis_height} - ${chain_height} 无报块`);
|
||||||
|
await this.redis.set(`${this.coin}:last_check`, chain_height);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await this.insert_blkreportprofit(suc_data);
|
||||||
|
await this.redis.set(`${this.coin}:last_check`, chain_height);
|
||||||
|
return;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReportEnx extends Report {
|
||||||
|
constructor(coin) {
|
||||||
|
const method = "report";
|
||||||
|
super(coin, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
async query_blkstats(height) {
|
||||||
|
try {
|
||||||
|
const yesterday = Times.utcTime(new Date().valueOf() - 24 * 60 * 60 * 1000)
|
||||||
|
.split(" ")[0]
|
||||||
|
.replace(/\-/g, "");
|
||||||
|
const table1 = "enx_pool_blkstats";
|
||||||
|
const table2 = `enx_pool_blkstats_${yesterday}`;
|
||||||
|
|
||||||
|
const queryTableExistSql = `SHOW TABLES LIKE ?;`;
|
||||||
|
const existData = await this.pooldb.exec(queryTableExistSql, [table2]);
|
||||||
|
|
||||||
|
if (existData.length === 0) {
|
||||||
|
const sql = `SELECT date, height, hash FROM ?? WHERE height > ?;`;
|
||||||
|
return await this.pooldb.exec(sql, [table1, height]);
|
||||||
|
} else {
|
||||||
|
const sql = `SELECT date, height, hash FROM ?? WHERE height > ?
|
||||||
|
UNION ALL
|
||||||
|
SELECT date, height, hash FROM ?? WHERE height > ?;`;
|
||||||
|
return await this.pooldb.exec(sql, [table1, height, table2, height]);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async query_blkreportprofit() {
|
||||||
|
try {
|
||||||
|
const sql = `SELECT MAX(height) AS max_height FROM ${this.coin}_blkreportprofitv2;`;
|
||||||
|
const result = await this.distribution.exec(sql);
|
||||||
|
if (result.length === 0 || result[0].max_height === null) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return result[0].max_height;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async insertinto_blkreportprofit(data) {
|
||||||
|
try {
|
||||||
|
let sql = `INSERT INTO ${this.coin}_blkreportprofitv2 (date, height, hash, reward, fees, state) VALUES `;
|
||||||
|
const values = [];
|
||||||
|
for (let item of data) {
|
||||||
|
const { date, height, hash, reward } = item;
|
||||||
|
values.push(date, height, hash, reward, 0, 1);
|
||||||
|
sql += `(?,?,?,?,?,?), `;
|
||||||
|
}
|
||||||
|
sql = sql.slice(0, -2);
|
||||||
|
await this.distribution.exec_transaction(sql, values);
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async main() {
|
||||||
|
// const start_time = Times.utcTime(new Date().valueOf());
|
||||||
|
const last_height = await this.query_blkreportprofit();
|
||||||
|
const data = await this.query_blkstats(last_height);
|
||||||
|
|
||||||
|
if (data.length === 0) {
|
||||||
|
console.log(`${this.coin} 无报块`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const block_data = []
|
||||||
|
for(let item of data){
|
||||||
|
const {date, height, hash} = item
|
||||||
|
block_data.push({date, height, hash, reward:333})
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.insertinto_blkreportprofit(block_data);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Report,
|
||||||
|
ReportEnx,
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
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;
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,154 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Verify</title>
|
||||||
|
<script src="./elementui/axios.min.js"></script>
|
||||||
|
<script src="./elementui/echarts.min.js"></script>
|
||||||
|
<!-- 引入vue -->
|
||||||
|
<script src="./elementui/vue.min.js"></script>
|
||||||
|
<!-- 引入样式 -->
|
||||||
|
<link rel="stylesheet" href="./elementui/index.css">
|
||||||
|
<!-- 引入组件库 -->
|
||||||
|
<script src="./elementui/index.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app">
|
||||||
|
<template>
|
||||||
|
|
||||||
|
<div style="width: 200px;">
|
||||||
|
<el-input v-model="input_date" placeholder="请输入日期(2024-12-01格式)" size="mini" @keyup.enter="get_date"></el-input>
|
||||||
|
<el-button size="mini" type="primary" @click="get_date">查询</el-button>
|
||||||
|
</div>
|
||||||
|
<!-- <h6>1,"分配数据表":按币种分组,每个币种单独计算除"pool_account"账号之外的"分配金额"比例,所有除"pool_account"账号以外的用户总比例为95%,即每个账号比例需要额外*0.95的权重</h6>
|
||||||
|
<h6>2,"算力数据表":按币种分组,每个币种单独计算所有用户算力占比,这个无需权重,直接算即可</h6>
|
||||||
|
<h6>3,比较"分配占比"和"算力占比"是否一致,币种+账号名相同的对比</h6>
|
||||||
|
<h6>4,"报块数据表":将上方算出的算力数据占比,找到对应币种的报块数据,用每个</h6> -->
|
||||||
|
<div style="margin-top:50px;width: 1000px;">
|
||||||
|
<h3>分配数据</h3>
|
||||||
|
<el-table :data="distribution" style="width: 100%">
|
||||||
|
<!-- <el-table-column prop="date" label="日期" width="180">
|
||||||
|
</el-table-column> -->
|
||||||
|
<el-table-column prop="coin" label="币种" width="100">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="user" label="用户" width="180">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="amount" label="分配金额" width="200">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="address" label="钱包地址">
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top:150px; width: 800px;">
|
||||||
|
<h3>算力数据</h3>
|
||||||
|
<el-table :data="hashrate" style="width: 100%">
|
||||||
|
<!-- <el-table-column prop="date" label="日期" width="180">
|
||||||
|
</el-table-column> -->
|
||||||
|
<el-table-column prop="coin" label="币种" width="100">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="user" label="用户" width="100">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="mhs24h" label="过去24小时算力">
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top:50px;width: 800px;">
|
||||||
|
<h3>报块数据</h3>
|
||||||
|
<el-table :data="reward" style="width: 100%">
|
||||||
|
<!-- <el-table-column prop="date" label="日期" width="180">
|
||||||
|
</el-table-column> -->
|
||||||
|
<el-table-column prop="coin" label="币种" width="100">
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="reward" label="总报块奖励">
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
async function get(api, params) {
|
||||||
|
try {
|
||||||
|
const response = await axios({
|
||||||
|
method: "GET",
|
||||||
|
url: `http://13.214.175.13:23116/api/${api}`,
|
||||||
|
params
|
||||||
|
});
|
||||||
|
return response.data; // 提取响应数据
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const Main = {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
input_date: "",
|
||||||
|
distribution: [],
|
||||||
|
hashrate: [],
|
||||||
|
reward: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async query_distribution(date) {
|
||||||
|
try {
|
||||||
|
const data = await get("distribution", { date });
|
||||||
|
return data || []; // 确保返回的是数组
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async query_blockreward(date) {
|
||||||
|
try {
|
||||||
|
const data = await get("reward", { date });
|
||||||
|
return data || [];
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async query_hashrate(date) {
|
||||||
|
try {
|
||||||
|
const data = await get("hashrate", { date });
|
||||||
|
return data || [];
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async get_date() {
|
||||||
|
const regex = /^\d{4}-\d{2}-\d{2}$/; // 匹配 yyyy-MM-dd 格式
|
||||||
|
if (!regex.test(this.input_date)) {
|
||||||
|
this.$message.error('日期格式无效,请使用 yyyy-MM-dd 格式'); // 显示错误消息
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 并发请求所有数据
|
||||||
|
const [distribution, hashrate, reward] = await Promise.all([
|
||||||
|
this.query_distribution(this.input_date),
|
||||||
|
this.query_hashrate(this.input_date),
|
||||||
|
this.query_blockreward(this.input_date)
|
||||||
|
]);
|
||||||
|
// 赋值数据
|
||||||
|
this.distribution = distribution;
|
||||||
|
this.hashrate = hashrate;
|
||||||
|
this.reward = reward;
|
||||||
|
} catch (error) {
|
||||||
|
this.$message.error('数据加载失败,请检查网络或接口!');
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const Ctor = Vue.extend(Main);
|
||||||
|
new Ctor().$mount('#app'); // 确保 Vue 实例已挂载
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,88 @@
|
||||||
|
const mysql = require("mysql2/promise");
|
||||||
|
|
||||||
|
class DBPool {
|
||||||
|
constructor(coin, options) {
|
||||||
|
this.coin = coin;
|
||||||
|
this.pool = mysql.createPool(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 非事务SQL操作
|
||||||
|
* @param {String} sql
|
||||||
|
* @param {Array} values
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async exec(sql, values = []) {
|
||||||
|
const con = await this.pool.getConnection();
|
||||||
|
try {
|
||||||
|
const [data] = await con.query(sql, values);
|
||||||
|
return data;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
con.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 单独SQL事务操作
|
||||||
|
* @param {String} sql
|
||||||
|
* @param {Array} values
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async exec_transaction(sql, values = []) {
|
||||||
|
const con = await this.pool.getConnection();
|
||||||
|
try {
|
||||||
|
await con.beginTransaction();
|
||||||
|
const [data] = await con.query(sql, values);
|
||||||
|
await con.commit();
|
||||||
|
return data;
|
||||||
|
} catch (err) {
|
||||||
|
await con.rollback();
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
con.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行一系列 SQL 操作,合并到一个事务中。
|
||||||
|
* @param {Array} params [{sql:"", param:[param1, param2, ...]}]
|
||||||
|
*/
|
||||||
|
async exec_transaction_together(params) {
|
||||||
|
const con = await this.pool.getConnection();
|
||||||
|
try {
|
||||||
|
await con.beginTransaction();
|
||||||
|
|
||||||
|
// 确保所有查询都完成后再提交事务
|
||||||
|
for (const { sql, param } of params) {
|
||||||
|
await con.query(sql, param);
|
||||||
|
}
|
||||||
|
|
||||||
|
await con.commit();
|
||||||
|
} catch (err) {
|
||||||
|
await con.rollback();
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
con.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async exec_write_lock(sql, values = [], table_name) {
|
||||||
|
const con = await this.pool.getConnection();
|
||||||
|
try {
|
||||||
|
await con.beginTransaction();
|
||||||
|
// 使用模板字符串而不是占位符来构建 LOCK TABLES 语句
|
||||||
|
await con.query(`LOCK TABLES ${table_name} WRITE`);
|
||||||
|
await con.query(sql, values);
|
||||||
|
await con.commit();
|
||||||
|
} catch (err) {
|
||||||
|
await con.rollback();
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
await con.query("UNLOCK TABLES");
|
||||||
|
con.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = DBPool
|
|
@ -0,0 +1,102 @@
|
||||||
|
const Times = require("./times")
|
||||||
|
const DBPool = require("./mysql")
|
||||||
|
|
||||||
|
class Service {
|
||||||
|
constructor(){
|
||||||
|
this.hashratedb = new DBPool("nexa", {
|
||||||
|
"host": "13.213.4.56",
|
||||||
|
"user": "root",
|
||||||
|
"password": "123456",
|
||||||
|
"database": "hashrate",
|
||||||
|
"port":6789,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
})
|
||||||
|
|
||||||
|
this.distributiondb = new DBPool("nexa", {
|
||||||
|
"host": "13.213.4.56",
|
||||||
|
"user": "root",
|
||||||
|
"password": "123456",
|
||||||
|
"database": "distribution",
|
||||||
|
"port":6789,
|
||||||
|
"waitForConnections": true,
|
||||||
|
"connectionLimit": 20,
|
||||||
|
"queueLimit": 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async query_distribution(date){
|
||||||
|
date = date + " 00:00:00"
|
||||||
|
try{
|
||||||
|
const sql = `SELECT create_date AS date, coin, user, amount, address FROM wallet_in WHERE create_date = ?;`
|
||||||
|
const data = await this.distributiondb.exec(sql, [date])
|
||||||
|
if(!data || data.length === 0){
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
} catch(err){
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async query_hashrate(date){
|
||||||
|
date = date + " 00:00:00"
|
||||||
|
try{
|
||||||
|
const coins = ["nexa", "grs", "mona", "dgbo", "dgbq", "dgbs", "rxd"]
|
||||||
|
const result = []
|
||||||
|
for(let i=0; i<coins.length; i++){
|
||||||
|
const sql = `SELECT date, user, SUM(mhs24h) AS mhs24h FROM ${coins[i]}_mhsv2 WHERE date = ? GROUP BY date, user;`
|
||||||
|
const data = await this.hashratedb.exec(sql, [date])
|
||||||
|
for(let item of data){
|
||||||
|
result.push({date:item.date, user:item.user, coin: coins[i], mhs24h:item.mhs24h})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!result || result.length === 0){
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
} catch(err){
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async query_total_blockreward(date){
|
||||||
|
date = date + " 00:00:00"
|
||||||
|
let param_date = Times.utcTime(new Date(date).valueOf() - 86400000)
|
||||||
|
param_date = param_date.split(" ")[0]
|
||||||
|
try{
|
||||||
|
const coins = ["nexa", "grs", "mona", "dgbo", "dgbq", "dgbs", "rxd"]
|
||||||
|
const result = []
|
||||||
|
for(let i=0; i<coins.length; i++){
|
||||||
|
const sql = `SELECT SUM(reward) AS reward FROM ${coins[i]}_blkreportprofitv2 WHERE DATE(date) = ? AND state = ?;`
|
||||||
|
const data = await this.distributiondb.exec(sql, [param_date, 1])
|
||||||
|
for(let item of data){
|
||||||
|
result.push({date, coin:coins[i], reward:item.reward})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!result || result.length === 0){
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
} catch(err){
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async query_walletout(date){
|
||||||
|
date = date + " 00:00:00"
|
||||||
|
try{
|
||||||
|
const sql = `SELECT * FROM wallet_outv2 WHERE date = ?;`
|
||||||
|
const data = await this.distributiondb.exec(sql, [date])
|
||||||
|
if(!data || data.length === 0){
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
} catch(err){
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Service
|
|
@ -0,0 +1,52 @@
|
||||||
|
class Times {
|
||||||
|
static bjTime(dateForm) {
|
||||||
|
if (dateForm === "") {
|
||||||
|
//解决deteForm为空传1970-01-01 00:00:00
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
var dateee = new Date(dateForm).toJSON();
|
||||||
|
var date = new Date(+new Date(dateee) + 8 * 3600 * 1000)
|
||||||
|
.toISOString()
|
||||||
|
.replace(/T/g, " ")
|
||||||
|
.replace(/\.[\d]{3}Z/, "");
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static utcTime(dateForm) {
|
||||||
|
if (dateForm === "") {
|
||||||
|
//解决deteForm为空传1970-01-01 00:00:00
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
var dateee = new Date(dateForm).toJSON();
|
||||||
|
var date = new Date(+new Date(dateee))
|
||||||
|
.toISOString()
|
||||||
|
.replace(/T/g, " ")
|
||||||
|
.replace(/\.[\d]{3}Z/, "");
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static times() {
|
||||||
|
const date = new Date();
|
||||||
|
const y = date.getFullYear();
|
||||||
|
const M = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const d = String(date.getDate()).padStart(2, '0');
|
||||||
|
const h = String(date.getHours()).padStart(2, '0');
|
||||||
|
const m = String(date.getMinutes()).padStart(2, '0');
|
||||||
|
const s = String(date.getSeconds()).padStart(2, '0');
|
||||||
|
|
||||||
|
return [
|
||||||
|
`${y}-${M}-${d} ${h}:${m}`, // 格式:YYYY-MM-DD HH:mm
|
||||||
|
`${y}-${M}-${d} ${h}:${m}:${s}`, // 格式:YYYY-MM-DD HH:mm:ss
|
||||||
|
`${y}-${M}-${d} ${h}`, // 格式:YYYY-MM-DD HH
|
||||||
|
`${m}`, // 分钟格式:mm
|
||||||
|
`${h}`, // 小时格式:HH
|
||||||
|
`${d}` // 日期格式:DD
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Times;
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
server {
|
||||||
|
listen 23116; # 监听的端口号
|
||||||
|
server_name localhost; # 您的域名
|
||||||
|
|
||||||
|
root /var/www/html/verify; # 设置根路径为 /var/www/html/web
|
||||||
|
|
||||||
|
index index.html; # 设置默认索引文件
|
||||||
|
|
||||||
|
location / {
|
||||||
|
root /var/www/html/verify;
|
||||||
|
index index.html index.htm;
|
||||||
|
# try_files $uri $uri/ @router; # 处理请求的方式
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#location @router{
|
||||||
|
# rewrite ^.*$ /index.html last;
|
||||||
|
#}
|
||||||
|
|
||||||
|
|
||||||
|
# 静态资源配置
|
||||||
|
location ~ ^/(images|javascript|js|css|flash|media|static|eot|otf|ttf|woff|svg|woof2|fonts)/{
|
||||||
|
root /var/www/html/verify;
|
||||||
|
add_header Access-Control-Allow-Origin *;
|
||||||
|
|
||||||
|
#过期30天,静态文件不怎么更新,过期可以设大一点,如果频繁更新,则可以设置得小一点。
|
||||||
|
expires 30d;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ ^/favicon\.ico$ {
|
||||||
|
root /var/www/html/verify;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($args ~* "select|insert|update|delete|drop|exec|script"){
|
||||||
|
return 403;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
const express = require("express");
|
||||||
|
const app = express();
|
||||||
|
const Service = require("./service");
|
||||||
|
|
||||||
|
const service = new Service();
|
||||||
|
const port = process.argv[2] || 23115
|
||||||
|
|
||||||
|
// Middleware to parse JSON and form data
|
||||||
|
app.use(express.json()); // for parsing application/json
|
||||||
|
app.use(express.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
|
||||||
|
// 跨域处理
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
res.header("Access-Control-Allow-Origin", "*");
|
||||||
|
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
|
||||||
|
res.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, PATCH, DELETE");
|
||||||
|
// res.header('Content-Type', 'application/json')
|
||||||
|
res.header("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
||||||
|
res.header("Access-Control-Allow-Credentials", true);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 连通测试
|
||||||
|
app.get("/test", (req, res) => {
|
||||||
|
res.json({ msg: "hello!" });
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/distribution", async (req, res) => {
|
||||||
|
const { date } = req.query;
|
||||||
|
const data = await service.query_distribution(date);
|
||||||
|
let result;
|
||||||
|
if (!data) {
|
||||||
|
result = [];
|
||||||
|
} else {
|
||||||
|
result = data;
|
||||||
|
}
|
||||||
|
res.json(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/hashrate", async (req, res) => {
|
||||||
|
const { date } = req.query;
|
||||||
|
const data = await service.query_hashrate(date);
|
||||||
|
let result;
|
||||||
|
if (!data) {
|
||||||
|
result = [];
|
||||||
|
} else {
|
||||||
|
result = data;
|
||||||
|
}
|
||||||
|
res.json(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/reward", async (req, res) => {
|
||||||
|
const { date } = req.query;
|
||||||
|
const data = await service.query_total_blockreward(date);
|
||||||
|
let result;
|
||||||
|
if (!data) {
|
||||||
|
result = [];
|
||||||
|
} else {
|
||||||
|
result = data;
|
||||||
|
}
|
||||||
|
res.json(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/walletout", async (req, res) => {
|
||||||
|
const { date } = req.query;
|
||||||
|
const data = await service.query_walletout(date);
|
||||||
|
let result;
|
||||||
|
if (!data) {
|
||||||
|
result = [];
|
||||||
|
} else {
|
||||||
|
result = data;
|
||||||
|
}
|
||||||
|
res.json(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(port, () => {
|
||||||
|
console.log(`Server is listening at http://127.0.0.1:${port}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
[
|
||||||
|
{ date: "2024-12-03", coin: "nexa", reward: "2700041722.75000000" },
|
||||||
|
{ date: "2024-12-03", coin: "grs", reward: "2285.22102434" },
|
||||||
|
{ date: "2024-12-03", coin: "mona", reward: "5644.34314098" },
|
||||||
|
{ date: "2024-12-03", coin: "dgbo", reward: "363953.84122200" },
|
||||||
|
{ date: "2024-12-03", coin: "dgbq", reward: "303936.09476747" },
|
||||||
|
{ date: "2024-12-03", coin: "dgbs", reward: "103023.94566538" },
|
||||||
|
{ date: "2024-12-03", coin: "rxd", reward: "2150203.36261784" },
|
||||||
|
]
|
||||||
|
[({ date: "2024-12-03T00:00:00.000Z", user: "a20", coin: "nexa", mhs24h: "1221097.498765" }, { date: "2024-12-03T00:00:00.000Z", user: "a21", coin: "nexa", mhs24h: "811679.870615" }, { date: "2024-12-03T00:00:00.000Z", user: "a2x", coin: "nexa", mhs24h: "125128.418926" }, { date: "2024-12-03T00:00:00.000Z", user: "n1gminer", coin: "grs", mhs24h: "12952631.953375" }, { date: "2024-12-03T00:00:00.000Z", user: "n3miner", coin: "mona", mhs24h: "36491507.736254" }, { date: "2024-12-03T00:00:00.000Z", user: "b20miner", coin: "dgbo", mhs24h: "14829610.388251" }, { date: "2024-12-03T00:00:00.000Z", user: "w3miner", coin: "dgbo", mhs24h: "3356677.306375" }, { date: "2024-12-03T00:00:00.000Z", user: "a1xqminer", coin: "dgbq", mhs24h: "48256381.780041" }, { date: "2024-12-03T00:00:00.000Z", user: "miner", coin: "dgbq", mhs24h: "0.000000" }, { date: "2024-12-03T00:00:00.000Z", user: "n1qminer", coin: "dgbq", mhs24h: "32414166.811899" }, { date: "2024-12-03T00:00:00.000Z", user: "a1xsminer", coin: "dgbs", mhs24h: "317211396.182949" }, { date: "2024-12-03T00:00:00.000Z", user: "n1sminer", coin: "dgbs", mhs24h: "32972685.343203" }, { date: "2024-12-03T00:00:00.000Z", user: "a1xrminer", coin: "rxd", mhs24h: "1049483151.952393" }, { date: "2024-12-03T00:00:00.000Z", user: "miner", coin: "rxd", mhs24h: "0.000000" })];
|
|
@ -0,0 +1,12 @@
|
||||||
|
const Times = require('../public/times')
|
||||||
|
const str = "rxd_miners_stats_20241128"
|
||||||
|
|
||||||
|
let sql = `DROP TABLE IF EXISTS `
|
||||||
|
const start = new Date("2024-11-28 00:00:00").valueOf()
|
||||||
|
for(let i=0; i<70; i++){
|
||||||
|
const t = Times.utcTime(start + i * 86400000)
|
||||||
|
const ymd = t.split(" ")[0].replace(/-/g, "")
|
||||||
|
sql += ``+ `rxd_miners_stats_${ymd},`
|
||||||
|
}
|
||||||
|
sql = sql.slice(0, -1)
|
||||||
|
console.log(sql);
|
|
@ -0,0 +1,340 @@
|
||||||
|
const Times = require("../public/times");
|
||||||
|
// const executeWithRetry = require("./public/retry")
|
||||||
|
const Init = require("./init");
|
||||||
|
|
||||||
|
class HashRate extends Init {
|
||||||
|
constructor(coin) {
|
||||||
|
const method = "hashrate";
|
||||||
|
super(coin, method);
|
||||||
|
this.count = 0;
|
||||||
|
this.diffOneShareHashsAvg = 2 ** 32 - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算hash
|
||||||
|
* @param {Number} accepts 时段内接受总数
|
||||||
|
* @param {Number} seconds 时段秒数
|
||||||
|
* @param {String} unit H/s、KH/s、MH/s、GH/s、TH/s、PH/s、EH/s
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
calculate_hashrate(accepts, seconds, unit) {
|
||||||
|
let num;
|
||||||
|
switch (unit) {
|
||||||
|
case "H/s":
|
||||||
|
num = 1;
|
||||||
|
break;
|
||||||
|
case "KH/s":
|
||||||
|
num = 1_000;
|
||||||
|
break;
|
||||||
|
case "MH/s":
|
||||||
|
num = 1_000_000;
|
||||||
|
break;
|
||||||
|
case "GH/s":
|
||||||
|
num = 1_000_000_000;
|
||||||
|
break;
|
||||||
|
case "TH/s":
|
||||||
|
num = 1_000_000_000_000;
|
||||||
|
break;
|
||||||
|
case "PH/s":
|
||||||
|
num = 1_000_000_000_000_000;
|
||||||
|
break;
|
||||||
|
case "EH/s":
|
||||||
|
num = 10 ** 18;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw `${unit}不是已知单位`;
|
||||||
|
}
|
||||||
|
const hashrate = (accepts * this.diffOneShareHashsAvg) / seconds / num;
|
||||||
|
return hashrate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将主、备查询出来的数据合并
|
||||||
|
* @param {*} data [{user:"", miner:"", accepts:100},{user:"", miner:"", accepts:100}...]
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
merge(data) {
|
||||||
|
// 创建一个 Map 来存储 user 和 miner 组合的结果
|
||||||
|
const results = new Map();
|
||||||
|
|
||||||
|
data.forEach((item) => {
|
||||||
|
const key = `${item.user}-${item.miner}`;
|
||||||
|
|
||||||
|
if (results.has(key)) {
|
||||||
|
const existing = results.get(key);
|
||||||
|
existing.accepts += parseFloat(item.accepts);
|
||||||
|
if (new Date(item.last_submit) > new Date(existing.last_submit)) {
|
||||||
|
existing.last_submit = item.last_submit;
|
||||||
|
}
|
||||||
|
results.set(key, existing);
|
||||||
|
} else {
|
||||||
|
results.set(key, {
|
||||||
|
user: item.user,
|
||||||
|
miner: item.miner,
|
||||||
|
accepts: parseFloat(item.accepts),
|
||||||
|
last_submit: item.last_submit,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 将结果转换为数组
|
||||||
|
const resultArray = Array.from(results.values());
|
||||||
|
return resultArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询主库符合时段的表名
|
||||||
|
* @param {String} start_time
|
||||||
|
* @param {String} end_time
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async query_table(start_time, end_time) {
|
||||||
|
try {
|
||||||
|
const sql = `(SELECT date, \`from\`, \`to\` FROM ${this.coin}_blk_height_detail WHERE date >= ? ORDER BY date LIMIT 1) UNION (SELECT date, \`from\`, \`to\` FROM ${this.coin}_blk_height_detail WHERE date >= ? AND date < ?) ORDER BY date;`;
|
||||||
|
const data = await this.sharesdb.exec(sql, [end_time, start_time, end_time]);
|
||||||
|
const result = [];
|
||||||
|
if (data.length !== 0) {
|
||||||
|
for (let item of data) {
|
||||||
|
result.push(`${this.coin}_block_detail_${item.from}_${Math.trunc(item.to - 1)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push(`${this.coin}_blk_detail`);
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询从库符合时段的表名
|
||||||
|
* @param {String} start_time
|
||||||
|
* @param {String} end_time
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async query_slave_table(start_time, end_time) {
|
||||||
|
try {
|
||||||
|
const sql = `(SELECT date, \`from\`, \`to\` FROM ${this.coin}_blk_height_detail WHERE date >= ? ORDER BY date LIMIT 1) UNION (SELECT date, \`from\`, \`to\` FROM ${this.coin}_blk_height_detail WHERE date >= ? AND date < ?) ORDER BY date;`;
|
||||||
|
const data = await this.sharesdb_slave.exec(sql, [end_time, start_time, end_time]);
|
||||||
|
const result = [];
|
||||||
|
if (data.length !== 0) {
|
||||||
|
for (let item of data) {
|
||||||
|
result.push(`${this.coin}_block_detail_${item.from}_${Math.trunc(item.to - 1)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push(`${this.coin}_blk_detail`);
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询时段内accepts,主从同时查询
|
||||||
|
async query_accepts(start_time, end_time, enable) {
|
||||||
|
try {
|
||||||
|
if (this.count === undefined) this.count = 0;
|
||||||
|
|
||||||
|
const generateUnionSql = (tables) => {
|
||||||
|
let sql = `SELECT MAX(date) AS last_submit, user, miner, SUM(miner_diff) AS accepts FROM ( `;
|
||||||
|
for (let i = 0; i < tables.length; i++) {
|
||||||
|
sql += `SELECT date, user, miner, miner_diff, pool_diff FROM ${tables[i]} WHERE date >= ? AND date < ?`;
|
||||||
|
if (i < tables.length - 1) {
|
||||||
|
sql += ` \nUNION ALL\n `;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sql += `) AS combined_tables GROUP BY user, miner;`;
|
||||||
|
return sql;
|
||||||
|
};
|
||||||
|
|
||||||
|
let sql, slave_sql;
|
||||||
|
if (enable) {
|
||||||
|
const [tables_name, slave_tables_name] = await Promise.all([this.query_table(start_time, end_time), this.query_slave_table(start_time, end_time)]);
|
||||||
|
|
||||||
|
sql = tables_name.length <= 1 ? `SELECT MAX(date) AS last_submit, user, miner, SUM(miner_diff) AS accepts FROM ${this.coin}_blk_detail WHERE date >= ? AND date < ? GROUP BY user, miner;` : generateUnionSql(tables_name);
|
||||||
|
|
||||||
|
slave_sql = slave_tables_name.length <= 1 ? `SELECT MAX(date) AS last_submit, user, miner, SUM(miner_diff) AS accepts FROM ${this.coin}_blk_detail WHERE date >= ? AND date < ? GROUP BY user, miner;` : generateUnionSql(slave_tables_name);
|
||||||
|
|
||||||
|
const [accepts_data, slave_accepts] = await Promise.all([this.sharesdb.exec(sql, [start_time, end_time]), this.sharesdb_slave.exec(slave_sql, [start_time, end_time])]);
|
||||||
|
|
||||||
|
return this.merge(accepts_data.concat(slave_accepts));
|
||||||
|
} else {
|
||||||
|
const tables_name = await this.query_table(start_time, end_time);
|
||||||
|
|
||||||
|
sql = tables_name.length <= 1 ? `SELECT MAX(date) AS last_submit, user, miner, SUM(miner_diff) AS accepts FROM ${this.coin}_blk_detail WHERE date >= ? AND date < ? GROUP BY user, miner;` : generateUnionSql(tables_name);
|
||||||
|
|
||||||
|
const accepts_data = await this.sharesdb.exec(sql, [start_time, end_time]);
|
||||||
|
return this.merge(accepts_data);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error in query_accepts: ${err.message}`);
|
||||||
|
await this.sleep(1000 * 15);
|
||||||
|
if (this.count > 3) { // 重试4次,1分钟
|
||||||
|
this.count = 0;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
this.count++;
|
||||||
|
return this.query_accepts(start_time, end_time, enable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询当天miners状态,排除掉超过1天没有提交的矿工
|
||||||
|
async query_miners(time) {
|
||||||
|
try {
|
||||||
|
const sql = `SELECT date, user, miner, state, ratio, last_submit FROM ${this.coin}_miners WHERE last_submit >= DATE(?) - INTERVAL 1 DAY;`;
|
||||||
|
const miners_state = await this.pooldb.exec(sql, [time]);
|
||||||
|
return miners_state;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async insert_mhs(data) {
|
||||||
|
if (data.length === 0 || !data || data.size === 0) {
|
||||||
|
console.log(Date.now(), ":30分钟没有新增矿机提交数据");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
let sql = `INSERT INTO ${this.coin}_mhsv2 (user, miner, date, mhs30m, mhs24h, state, last_submit) VALUES `;
|
||||||
|
const values = [];
|
||||||
|
data.forEach((item) => {
|
||||||
|
const { user, miner, date, mhs30m, mhs24h, state, last_submit } = item;
|
||||||
|
sql += `(?, ?, ?, ?, ?, ?, ?), `;
|
||||||
|
values.push(user, miner, date, mhs30m, mhs24h, state, last_submit);
|
||||||
|
});
|
||||||
|
sql = sql.slice(0, -2);
|
||||||
|
await this.hashratedb.exec_transaction(sql, values);
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async insert_mhs_real(data) {
|
||||||
|
if (data.length === 0 || !data || data.size === 0) {
|
||||||
|
console.log(Date.now(), ":5分钟没有新增矿机提交数据");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const del_sql = `DELETE FROM ${this.coin}_mhs_realv2 WHERE id > 0;`;
|
||||||
|
let sql = `INSERT INTO ${this.coin}_mhs_realv2 (user, miner, date, mhs30m, mhs24h, state, last_submit) VALUES `;
|
||||||
|
const values = [];
|
||||||
|
data.forEach((item) => {
|
||||||
|
const { user, miner, date, mhs30m, mhs24h, state, last_submit } = item;
|
||||||
|
sql += `(?, ?, ?, ?, ?, ? ,?), `;
|
||||||
|
values.push(user, miner, date, mhs30m, mhs24h, state, last_submit);
|
||||||
|
});
|
||||||
|
sql = sql.slice(0, -2);
|
||||||
|
// sql += ` AS new_values ON DUPLICATE KEY UPDATE date = new_values.date, mhs30m = new_values.mhs30m, mhs24h = new_values.mhs24h, state = new_values.state, last_submit = new_values.last_submit;`;
|
||||||
|
const sqls = [{ sql: del_sql }, { sql, param: values }];
|
||||||
|
await this.hashratedb.exec_transaction_together(sqls);
|
||||||
|
} catch (err) {
|
||||||
|
// 处理错误
|
||||||
|
console.error("Transaction failed: ", err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async query_hashrate_miners_accepts(end_time) {
|
||||||
|
try {
|
||||||
|
const ymd_last_30m = Times.utcTime(new Date(end_time).valueOf() - 1000 * 60 * 30);
|
||||||
|
const ymd_last_24h = Times.utcTime(new Date(end_time).valueOf() - 1000 * 60 * 60 * 24);
|
||||||
|
const state_sql = `SELECT t1.*
|
||||||
|
FROM ${this.coin}_minersv2 t1
|
||||||
|
INNER JOIN (
|
||||||
|
SELECT user, miner, MAX(date) AS max_date
|
||||||
|
FROM ${this.coin}_minersv2
|
||||||
|
WHERE date <= ?
|
||||||
|
GROUP BY user, miner
|
||||||
|
) t2
|
||||||
|
ON t1.user = t2.user AND t1.miner = t2.miner AND t1.date = t2.max_date;`;
|
||||||
|
const mhs30m_sql = `SELECT SUM(accepts) AS accepts_30min, user, miner FROM ${this.coin}_minersv2 WHERE date >= ? AND date < ? GROUP BY user, miner;`;
|
||||||
|
const mhs24h_sql = `SELECT SUM(accepts) AS accepts_24h, user, miner FROM ${this.coin}_minersv2 WHERE date >= ? AND date < ? GROUP BY user, miner;`;
|
||||||
|
const [state, mhs30m, mhs24h] = await Promise.all([this.hashratedb.exec(state_sql, [end_time]), this.hashratedb.exec(mhs30m_sql, [ymd_last_30m, end_time]), this.hashratedb.exec(mhs24h_sql, [ymd_last_24h, end_time])]);
|
||||||
|
|
||||||
|
const hashrate_map = new Map();
|
||||||
|
|
||||||
|
state.forEach((item) => {
|
||||||
|
const { date, user, miner, state, last_submit } = item;
|
||||||
|
hashrate_map.set(`${user}:${miner}`, { date: end_time, user, miner, state, last_submit, mhs30m: 0, mhs24h: 0 });
|
||||||
|
});
|
||||||
|
|
||||||
|
mhs30m.forEach((item) => {
|
||||||
|
const { accepts_30min, user, miner } = item;
|
||||||
|
|
||||||
|
const values = hashrate_map.get(`${user}:${miner}`);
|
||||||
|
|
||||||
|
values.mhs30m = this.calculate_hashrate(accepts_30min, 60 * 30, "MH/s");
|
||||||
|
|
||||||
|
hashrate_map.set(`${user}:${miner}`, values);
|
||||||
|
});
|
||||||
|
mhs24h.forEach((item) => {
|
||||||
|
const { accepts_24h, user, miner } = item;
|
||||||
|
const values = hashrate_map.get(`${user}:${miner}`);
|
||||||
|
|
||||||
|
values.mhs24h = this.calculate_hashrate(accepts_24h, 60 * 60 * 24, "MH/s");
|
||||||
|
|
||||||
|
hashrate_map.set(`${user}:${miner}`, values);
|
||||||
|
});
|
||||||
|
return hashrate_map;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async insert_hashrate_miners_table(end_time) {
|
||||||
|
try {
|
||||||
|
const ymd = end_time.split(":");
|
||||||
|
const date = ymd[0] + ":" + ymd[1] + ":" + "00";
|
||||||
|
// 计算最近5分钟accepts,最新矿机状态
|
||||||
|
const start_time = Times.utcTime(new Date(end_time).valueOf() - 1000 * 60 * 5);
|
||||||
|
let enable = (await this.redis.get(`${this.coin}:enable`)) || false;
|
||||||
|
|
||||||
|
let [accepts, miners_state] = await Promise.all([this.query_accepts(start_time, end_time, enable), this.query_miners(end_time)]);
|
||||||
|
|
||||||
|
// 创建nexa_miners表所需要的map
|
||||||
|
const miners_map = new Map();
|
||||||
|
// 判断各种情况
|
||||||
|
if (accepts.length === 0 && miners_state.length === 0) {
|
||||||
|
// 历史上没有矿工接入
|
||||||
|
return;
|
||||||
|
} else if (accepts.length !== 0 && miners_state.length === 0) {
|
||||||
|
// 主库出了问题,基本不可能出现这种情况
|
||||||
|
return;
|
||||||
|
} else if (accepts.length === 0 && miners_state.length !== 0) {
|
||||||
|
// 最近5分钟没有矿工接入,直接将m2pooldb-nexa_miners表中所有矿工的accepts更新为0,并放入nexa_miners表需要的map中
|
||||||
|
miners_state.forEach((item) => {
|
||||||
|
const { user, miner, state, last_submit } = item;
|
||||||
|
miners_map.set(`${user}:${miner}`, { date, user, miner, state: "offline", last_submit, accepts: 0 });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 先找到所有最近5分钟有提交的矿机
|
||||||
|
accepts.forEach((item) => {
|
||||||
|
const { user, miner, accepts, last_submit } = item;
|
||||||
|
miners_map.set(`${user}:${miner}`, { date, user, miner, accepts, last_submit, state: "online" });
|
||||||
|
});
|
||||||
|
// 再将stats表有记录矿机,但最近5分钟没有提交的矿机合并进去
|
||||||
|
miners_state.forEach((item) => {
|
||||||
|
const { user, miner, state, last_submit } = item;
|
||||||
|
if (!miners_map.get(`${user}:${miner}`)) {
|
||||||
|
miners_map.set(`${user}:${miner}`, { date, user, miner, accepts: 0, last_submit, state });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 将指定时段内的数据插入nexa_miners表
|
||||||
|
let insert_miners_table_sql = `INSERT INTO ${this.coin}_minersv2(user, miner, date, accepts, state, last_submit) VALUES `;
|
||||||
|
const miners_table_values = [];
|
||||||
|
miners_map.forEach((item) => {
|
||||||
|
const { user, miner, date, accepts, state, last_submit } = item;
|
||||||
|
insert_miners_table_sql += `(?, ?, ?, ?, ?, ?), `;
|
||||||
|
miners_table_values.push(user, miner, date, accepts, state, last_submit);
|
||||||
|
});
|
||||||
|
insert_miners_table_sql = insert_miners_table_sql.slice(0, -2);
|
||||||
|
await this.hashratedb.exec_transaction(insert_miners_table_sql, miners_table_values);
|
||||||
|
return;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = HashRate;
|
|
@ -0,0 +1,463 @@
|
||||||
|
const Times = require("../public/times");
|
||||||
|
const Decimal = require("decimal");
|
||||||
|
const fs = require("fs");
|
||||||
|
const DBPool = require("../lib/mysql");
|
||||||
|
const Cache = require("../lib/redis");
|
||||||
|
const { NEXARPCNode, GRSRPCNode, MONARPCNode, DGBRPCNode, RXDRPCNode } = require("../lib/node");
|
||||||
|
|
||||||
|
class Init {
|
||||||
|
constructor(coin, method) {
|
||||||
|
this.coin = coin;
|
||||||
|
const config = fs.readFileSync(`../config/${coin}.conf`, "utf-8");
|
||||||
|
const { master, slave, redis_options, node_options, distribution_conf, MAX_MATURE, REPORT_ADDRESS } = JSON.parse(config);
|
||||||
|
const { pooldb, sharesdb, distribution, hashrate, users_addresses, balance } = master;
|
||||||
|
const { pooldb_slave, sharesdb_slave } = slave;
|
||||||
|
const { node1, node2 } = node_options;
|
||||||
|
const { redis1 } = redis_options;
|
||||||
|
const { POOL_FEE } = distribution_conf;
|
||||||
|
const node_map = {
|
||||||
|
mona: MONARPCNode,
|
||||||
|
nexa: NEXARPCNode,
|
||||||
|
grs: GRSRPCNode,
|
||||||
|
dgbs: DGBRPCNode,
|
||||||
|
dgbq: DGBRPCNode,
|
||||||
|
dgbo: DGBRPCNode,
|
||||||
|
rxd: RXDRPCNode,
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (method) {
|
||||||
|
case "hashrate":
|
||||||
|
this.sharesdb = new DBPool(coin, sharesdb);
|
||||||
|
this.sharesdb_slave = new DBPool(coin, sharesdb_slave);
|
||||||
|
this.pooldb = new DBPool(coin, pooldb);
|
||||||
|
this.redis = new Cache(redis1);
|
||||||
|
// this.pooldb_slave = new DBPool(coin, pooldb_slave)
|
||||||
|
this.hashratedb = new DBPool(coin, hashrate);
|
||||||
|
break;
|
||||||
|
case "report":
|
||||||
|
this.REPORT_ADDRESS = REPORT_ADDRESS;
|
||||||
|
this.node = new node_map[coin](node2);
|
||||||
|
this.distribution = new DBPool(coin, distribution);
|
||||||
|
this.redis = new Cache(redis1);
|
||||||
|
break;
|
||||||
|
case "clear":
|
||||||
|
this.pooldb = new DBPool(coin, pooldb);
|
||||||
|
this.sharesdb = new DBPool(coin, sharesdb);
|
||||||
|
this.hashratedb = new DBPool(coin, hashrate);
|
||||||
|
break;
|
||||||
|
case "distribution":
|
||||||
|
this.pooldb = new DBPool(coin, pooldb);
|
||||||
|
this.hashratedb = new DBPool(coin, hashrate);
|
||||||
|
this.distributiondb = new DBPool(coin, distribution);
|
||||||
|
this.users_addresses = new DBPool(coin, users_addresses);
|
||||||
|
this.node = new node_map[coin](node2);
|
||||||
|
this.REPORT_ADDRESS = REPORT_ADDRESS;
|
||||||
|
this.POOL_FEE = POOL_FEE;
|
||||||
|
console.log(`当前手续费率为:${POOL_FEE}`);
|
||||||
|
// this.balance = new DBPool(coin, balance)
|
||||||
|
break;
|
||||||
|
case "balance":
|
||||||
|
this.distribution = new DBPool(coin, distribution);
|
||||||
|
this.balancedb = new DBPool(coin, balance);
|
||||||
|
break;
|
||||||
|
case "confirm":
|
||||||
|
this.MAX_MATURE = MAX_MATURE;
|
||||||
|
this.REPORT_ADDRESS = REPORT_ADDRESS;
|
||||||
|
this.node = new node_map[coin](node2);
|
||||||
|
this.distribution = new DBPool(coin, distribution);
|
||||||
|
this.pooldb = new DBPool(coin, pooldb);
|
||||||
|
break;
|
||||||
|
case "stats":
|
||||||
|
this.pooldb = new DBPool(coin, pooldb);
|
||||||
|
this.hashratedb = new DBPool(coin, hashrate);
|
||||||
|
this.distribution = new DBPool(coin, distribution);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw `暂不支持${method}方法 init`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(ms) {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class HashRate extends Init {
|
||||||
|
constructor(coin) {
|
||||||
|
const method = "hashrate";
|
||||||
|
super(coin, method);
|
||||||
|
this.diffOneShareHashsAvg = 2 ** 32 - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算hash
|
||||||
|
* @param {Number} accepts 时段内接受总数
|
||||||
|
* @param {Number} seconds 时段秒数
|
||||||
|
* @param {String} unit H/s、KH/s、MH/s、GH/s、TH/s、PH/s、EH/s
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
calculate_hashrate(accepts, seconds, unit) {
|
||||||
|
let num;
|
||||||
|
switch (unit) {
|
||||||
|
case "H/s":
|
||||||
|
num = 1;
|
||||||
|
break;
|
||||||
|
case "KH/s":
|
||||||
|
num = 1_000;
|
||||||
|
break;
|
||||||
|
case "MH/s":
|
||||||
|
num = 1_000_000;
|
||||||
|
break;
|
||||||
|
case "GH/s":
|
||||||
|
num = 1_000_000_000;
|
||||||
|
break;
|
||||||
|
case "TH/s":
|
||||||
|
num = 1_000_000_000_000;
|
||||||
|
break;
|
||||||
|
case "PH/s":
|
||||||
|
num = 1_000_000_000_000_000;
|
||||||
|
break;
|
||||||
|
case "EH/s":
|
||||||
|
num = 10 ** 18;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw `${unit}不是已知单位`;
|
||||||
|
}
|
||||||
|
const hashrate = (accepts * this.diffOneShareHashsAvg) / seconds / num;
|
||||||
|
return hashrate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将主、备查询出来的数据合并
|
||||||
|
* @param {*} data [{user:"", miner:"", accepts:100},{user:"", miner:"", accepts:100}...]
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
merge(data) {
|
||||||
|
// 创建一个 Map 来存储 user 和 miner 组合的结果
|
||||||
|
const results = new Map();
|
||||||
|
|
||||||
|
data.forEach((item) => {
|
||||||
|
const key = `${item.user}-${item.miner}`;
|
||||||
|
|
||||||
|
if (results.has(key)) {
|
||||||
|
const existing = results.get(key);
|
||||||
|
existing.accepts += parseFloat(item.accepts);
|
||||||
|
if (new Date(item.last_submit) > new Date(existing.last_submit)) {
|
||||||
|
existing.last_submit = item.last_submit;
|
||||||
|
}
|
||||||
|
results.set(key, existing);
|
||||||
|
} else {
|
||||||
|
results.set(key, {
|
||||||
|
user: item.user,
|
||||||
|
miner: item.miner,
|
||||||
|
accepts: parseFloat(item.accepts),
|
||||||
|
last_submit: item.last_submit,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 将结果转换为数组
|
||||||
|
const resultArray = Array.from(results.values());
|
||||||
|
return resultArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询主库符合时段的表名
|
||||||
|
* @param {String} start_time
|
||||||
|
* @param {String} end_time
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async query_table(start_time, end_time) {
|
||||||
|
try {
|
||||||
|
const sql = `(SELECT date, \`from\`, \`to\` FROM ${this.coin}_blk_height_detail WHERE date >= ? ORDER BY date LIMIT 1) UNION (SELECT date, \`from\`, \`to\` FROM ${this.coin}_blk_height_detail WHERE date >= ? AND date < ?) ORDER BY date;`;
|
||||||
|
const data = await this.sharesdb.exec(sql, [end_time, start_time, end_time])
|
||||||
|
const result = [];
|
||||||
|
if (data.length !== 0) {
|
||||||
|
for (let item of data) {
|
||||||
|
result.push(`${this.coin}_block_detail_${item.from}_${Math.trunc(item.to - 1)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push(`${this.coin}_blk_detail`);
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询从库符合时段的表名
|
||||||
|
* @param {String} start_time
|
||||||
|
* @param {String} end_time
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async query_slave_table(start_time, end_time) {
|
||||||
|
try {
|
||||||
|
const sql = `(SELECT date, \`from\`, \`to\` FROM ${this.coin}_blk_height_detail WHERE date >= ? ORDER BY date LIMIT 1) UNION (SELECT date, \`from\`, \`to\` FROM ${this.coin}_blk_height_detail WHERE date >= ? AND date < ?) ORDER BY date;`;
|
||||||
|
const data = await this.sharesdb_slave.exec(sql, [end_time, start_time, end_time])
|
||||||
|
const result = [];
|
||||||
|
if (data.length !== 0) {
|
||||||
|
for (let item of data) {
|
||||||
|
result.push(`${this.coin}_block_detail_${item.from}_${Math.trunc(item.to - 1)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push(`${this.coin}_blk_detail`);
|
||||||
|
return result;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询时段内accepts,主从同时查询
|
||||||
|
async query_accepts(start_time, end_time, enable) {
|
||||||
|
try {
|
||||||
|
if (this.count === undefined) this.count = 0;
|
||||||
|
if (enable) {
|
||||||
|
const [tables_name, slave_tables_name] = await Promise.all([this.query_table(start_time, end_time), this.query_slave_table(start_time, end_time)]);
|
||||||
|
|
||||||
|
// 查询主库符合条件的数据
|
||||||
|
let sql = ``;
|
||||||
|
if (tables_name.length <= 1) {
|
||||||
|
sql = `SELECT MAX(date) AS last_submit, user, miner, SUM(miner_diff) AS accepts FROM ${this.coin}_blk_detail WHERE date >= "${start_time}" AND date < "${end_time}" GROUP BY user, miner;`;
|
||||||
|
} else {
|
||||||
|
sql = `SELECT MAX(date) AS last_submit, user, miner, SUM(miner_diff) AS accepts FROM ( `;
|
||||||
|
for (let i = 0; i < tables_name.length; i++) {
|
||||||
|
if (i < tables_name.length - 1) {
|
||||||
|
sql += `SELECT date, user, miner, miner_diff, pool_diff FROM ${tables_name[i]} WHERE date >= "${start_time}" AND date < "${end_time}" \nUNION ALL\n`;
|
||||||
|
} else {
|
||||||
|
sql += `SELECT date, user, miner, miner_diff, pool_diff FROM ${tables_name[i]} WHERE date >= "${start_time}" AND date < "${end_time}") AS combined_tables GROUP BY user, miner;`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let slave_sql = ``;
|
||||||
|
if (slave_tables_name.length <= 1) {
|
||||||
|
slave_sql = `SELECT MAX(date) AS last_submit, user, miner, SUM(miner_diff) AS accepts FROM ${this.coin}_blk_detail WHERE date >= "${start_time}" AND date < "${end_time}" GROUP BY user, miner;`;
|
||||||
|
} else {
|
||||||
|
slave_sql = `SELECT MAX(date) AS last_submit, user, miner, SUM(miner_diff) AS accepts FROM ( `;
|
||||||
|
for (let i = 0; i < slave_tables_name.length; i++) {
|
||||||
|
if (i < slave_tables_name.length - 1) {
|
||||||
|
slave_sql += `SELECT date, user, miner, miner_diff, pool_diff FROM ${slave_tables_name[i]} WHERE date >= "${start_time}" AND date < "${end_time}" \nUNION ALL\n`;
|
||||||
|
} else {
|
||||||
|
slave_sql += `SELECT date, user, miner, miner_diff, pool_diff FROM ${slave_tables_name[i]} WHERE date >= "${start_time}" AND date < "${end_time}") AS combined_tables GROUP BY user, miner;`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// // 执行查询,并将结果合并
|
||||||
|
const [accepts_data, slave_accepts] = await Promise.all([this.sharesdb.exec(sql), this.sharesdb_slave.exec(slave_sql)]);
|
||||||
|
const accepts = this.merge(accepts_data.concat(slave_accepts)); // 合并主备accepts
|
||||||
|
return accepts;
|
||||||
|
} else {
|
||||||
|
const tables_name = await this.query_table(start_time, end_time);
|
||||||
|
let sql = ``;
|
||||||
|
if (tables_name.length <= 1) {
|
||||||
|
sql = `SELECT MAX(date) AS last_submit, user, miner, SUM(miner_diff) AS accepts FROM ${this.coin}_blk_detail WHERE date >= "${start_time}" AND date < "${end_time}" GROUP BY user, miner;`;
|
||||||
|
} else {
|
||||||
|
sql = `SELECT MAX(date) AS last_submit, user, miner, SUM(miner_diff) AS accepts FROM ( `;
|
||||||
|
for (let i = 0; i < tables_name.length; i++) {
|
||||||
|
if (i < tables_name.length - 1) {
|
||||||
|
sql += `SELECT date, user, miner, miner_diff, pool_diff FROM ${tables_name[i]} WHERE date >= "${start_time}" AND date < "${end_time}" \nUNION ALL\n`;
|
||||||
|
} else {
|
||||||
|
sql += `SELECT date, user, miner, miner_diff, pool_diff FROM ${tables_name[i]} WHERE date >= "${start_time}" AND date < "${end_time}") AS combined_tables GROUP BY user, miner;`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const accepts_data = await this.sharesdb.exec(sql);
|
||||||
|
const slave_accepts = [];
|
||||||
|
const accepts = this.merge(accepts_data.concat(slave_accepts)); // 合并主备accepts
|
||||||
|
return accepts;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error in query_accepts: ${err.message}`);
|
||||||
|
await this.sleep(1000 * 15);
|
||||||
|
if (this.count > 3) { // 重试4次,1分钟
|
||||||
|
this.count = 0;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
this.count++;
|
||||||
|
return this.query_accepts(start_time, end_time, enable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询当天miners状态,排除掉超过1天没有提交的矿工
|
||||||
|
async query_miners(time) {
|
||||||
|
try {
|
||||||
|
const sql = `SELECT date, user, miner, state, ratio, last_submit FROM ${this.coin}_miners WHERE last_submit >= DATE(?) - INTERVAL 1 DAY;`;
|
||||||
|
const miners_state = await this.pooldb.exec(sql, [time]);
|
||||||
|
return miners_state;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async insert_mhs(data) {
|
||||||
|
if (data.length === 0 || !data || data.size === 0) {
|
||||||
|
console.log(Date.now(), ":30分钟没有新增矿机提交数据");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
let sql = `INSERT INTO ${this.coin}_mhsv2 (user, miner, date, mhs30m, mhs24h, state, last_submit) VALUES `;
|
||||||
|
const values = [];
|
||||||
|
data.forEach((item) => {
|
||||||
|
const { user, miner, date, mhs30m, mhs24h, state, last_submit } = item;
|
||||||
|
sql += `(?, ?, ?, ?, ?, ?, ?), `;
|
||||||
|
values.push(user, miner, date, mhs30m, mhs24h, state, last_submit);
|
||||||
|
});
|
||||||
|
sql = sql.slice(0, -2);
|
||||||
|
await this.hashratedb.exec_transaction(sql, values);
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async insert_mhs_real(data) {
|
||||||
|
if (data.length === 0 || !data || data.size === 0) {
|
||||||
|
console.log(Date.now(), ":5分钟没有新增矿机提交数据");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const del_sql = `DELETE FROM ${this.coin}_mhs_realv2 WHERE id > 0;`;
|
||||||
|
let sql = `INSERT INTO ${this.coin}_mhs_realv2 (user, miner, date, mhs30m, mhs24h, state, last_submit) VALUES `;
|
||||||
|
const values = [];
|
||||||
|
data.forEach((item) => {
|
||||||
|
const { user, miner, date, mhs30m, mhs24h, state, last_submit } = item;
|
||||||
|
sql += `(?, ?, ?, ?, ?, ? ,?), `;
|
||||||
|
values.push(user, miner, date, mhs30m, mhs24h, state, last_submit);
|
||||||
|
});
|
||||||
|
sql = sql.slice(0, -2);
|
||||||
|
// sql += ` AS new_values ON DUPLICATE KEY UPDATE date = new_values.date, mhs30m = new_values.mhs30m, mhs24h = new_values.mhs24h, state = new_values.state, last_submit = new_values.last_submit;`;
|
||||||
|
const sqls = [{ sql: del_sql }, { sql, param: values }];
|
||||||
|
await this.hashratedb.exec_transaction_together(sqls);
|
||||||
|
} catch (err) {
|
||||||
|
// 处理错误
|
||||||
|
console.error("Transaction failed: ", err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async query_hashrate_miners_accepts(end_time) {
|
||||||
|
try {
|
||||||
|
const ymd_last_30m = Times.utcTime(new Date(end_time).valueOf() - 1000 * 60 * 30);
|
||||||
|
const ymd_last_24h = Times.utcTime(new Date(end_time).valueOf() - 1000 * 60 * 60 * 24);
|
||||||
|
const state_sql = `SELECT t1.*
|
||||||
|
FROM ${this.coin}_minersv2 t1
|
||||||
|
INNER JOIN (
|
||||||
|
SELECT user, miner, MAX(date) AS max_date
|
||||||
|
FROM ${this.coin}_minersv2
|
||||||
|
WHERE date <= ?
|
||||||
|
GROUP BY user, miner
|
||||||
|
) t2
|
||||||
|
ON t1.user = t2.user AND t1.miner = t2.miner AND t1.date = t2.max_date;`;
|
||||||
|
const mhs30m_sql = `SELECT SUM(accepts) AS accepts_30min, user, miner FROM ${this.coin}_minersv2 WHERE date >= ? AND date < ? GROUP BY user, miner;`;
|
||||||
|
const mhs24h_sql = `SELECT SUM(accepts) AS accepts_24h, user, miner FROM ${this.coin}_minersv2 WHERE date >= ? AND date < ? GROUP BY user, miner;`;
|
||||||
|
const [state, mhs30m, mhs24h] = await Promise.all([this.hashratedb.exec(state_sql, [end_time]), this.hashratedb.exec(mhs30m_sql, [ymd_last_30m, end_time]), this.hashratedb.exec(mhs24h_sql, [ymd_last_24h, end_time])]);
|
||||||
|
|
||||||
|
const hashrate_map = new Map();
|
||||||
|
|
||||||
|
state.forEach((item) => {
|
||||||
|
const { date, user, miner, state, last_submit } = item;
|
||||||
|
hashrate_map.set(`${user}:${miner}`, { date: end_time, user, miner, state, last_submit, mhs30m: 0, mhs24h: 0 });
|
||||||
|
});
|
||||||
|
|
||||||
|
mhs30m.forEach((item) => {
|
||||||
|
const { accepts_30min, user, miner } = item;
|
||||||
|
|
||||||
|
const values = hashrate_map.get(`${user}:${miner}`);
|
||||||
|
|
||||||
|
values.mhs30m = this.calculate_hashrate(accepts_30min, 60 * 30, "MH/s");
|
||||||
|
|
||||||
|
hashrate_map.set(`${user}:${miner}`, values);
|
||||||
|
});
|
||||||
|
mhs24h.forEach((item) => {
|
||||||
|
const { accepts_24h, user, miner } = item;
|
||||||
|
const values = hashrate_map.get(`${user}:${miner}`);
|
||||||
|
|
||||||
|
values.mhs24h = this.calculate_hashrate(accepts_24h, 60 * 60 * 24, "MH/s");
|
||||||
|
|
||||||
|
hashrate_map.set(`${user}:${miner}`, values);
|
||||||
|
});
|
||||||
|
return hashrate_map;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async insert_hashrate_miners_table(end_time) {
|
||||||
|
try {
|
||||||
|
const ymd = end_time.split(":");
|
||||||
|
const date = ymd[0] + ":" + ymd[1] + ":" + "00";
|
||||||
|
// 计算最近5分钟accepts,最新矿机状态
|
||||||
|
const start_time = Times.utcTime(new Date(end_time).valueOf() - 1000 * 60 * 5);
|
||||||
|
let enable = (await this.redis.get(`${this.coin}:enable`)) || false;
|
||||||
|
|
||||||
|
let [accepts, miners_state] = await Promise.all([this.query_accepts(start_time, end_time, enable), this.query_miners(end_time)]);
|
||||||
|
|
||||||
|
// 创建nexa_miners表所需要的map
|
||||||
|
const miners_map = new Map();
|
||||||
|
// 判断各种情况
|
||||||
|
if (accepts.length === 0 && miners_state.length === 0) {
|
||||||
|
// 历史上没有矿工接入
|
||||||
|
return;
|
||||||
|
} else if (accepts.length !== 0 && miners_state.length === 0) {
|
||||||
|
// 主库出了问题,基本不可能出现这种情况
|
||||||
|
return;
|
||||||
|
} else if (accepts.length === 0 && miners_state.length !== 0) {
|
||||||
|
// 最近5分钟没有矿工接入,直接将m2pooldb-nexa_miners表中所有矿工的accepts更新为0,并放入nexa_miners表需要的map中
|
||||||
|
miners_state.forEach((item) => {
|
||||||
|
const { user, miner, state, last_submit } = item;
|
||||||
|
miners_map.set(`${user}:${miner}`, { date, user, miner, state: "offline", last_submit, accepts: 0 });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 先找到所有最近5分钟有提交的矿机
|
||||||
|
accepts.forEach((item) => {
|
||||||
|
const { user, miner, accepts, last_submit } = item;
|
||||||
|
miners_map.set(`${user}:${miner}`, { date, user, miner, accepts, last_submit, state: "online" });
|
||||||
|
});
|
||||||
|
// 再将stats表有记录矿机,但最近5分钟没有提交的矿机合并进去
|
||||||
|
miners_state.forEach((item) => {
|
||||||
|
const { user, miner, state, last_submit } = item;
|
||||||
|
if (!miners_map.get(`${user}:${miner}`)) {
|
||||||
|
miners_map.set(`${user}:${miner}`, { date, user, miner, accepts: 0, last_submit, state });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 将指定时段内的数据插入nexa_miners表
|
||||||
|
let insert_miners_table_sql = `INSERT INTO ${this.coin}_minersv2(user, miner, date, accepts, state, last_submit) VALUES `;
|
||||||
|
const miners_table_values = [];
|
||||||
|
miners_map.forEach((item) => {
|
||||||
|
const { user, miner, date, accepts, state, last_submit } = item;
|
||||||
|
insert_miners_table_sql += `(?, ?, ?, ?, ?, ?), `;
|
||||||
|
miners_table_values.push(user, miner, date, accepts, state, last_submit);
|
||||||
|
});
|
||||||
|
insert_miners_table_sql = insert_miners_table_sql.slice(0, -2);
|
||||||
|
await this.hashratedb.exec_transaction(insert_miners_table_sql, miners_table_values);
|
||||||
|
return;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const coin = "enx";
|
||||||
|
const hashrate = new HashRate(coin);
|
||||||
|
// schedule.scheduleJob({ minute: [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55], second: [30] }, async () => {
|
||||||
|
|
||||||
|
// });
|
||||||
|
async function main(){
|
||||||
|
const ymd_now = Times.utcTime(Date.now().valueOf());
|
||||||
|
const ymd = ymd_now.split(":");
|
||||||
|
const end_time = ymd[0] + ":" + ymd[1] + ":" + "00";
|
||||||
|
await hashrate.insert_hashrate_miners_table(end_time);
|
||||||
|
const currentMinute = new Date().getMinutes();
|
||||||
|
|
||||||
|
const data = await hashrate.query_hashrate_miners_accepts(end_time);
|
||||||
|
if (currentMinute === 0 || currentMinute === 30) {
|
||||||
|
await hashrate.insert_mhs(data);
|
||||||
|
await hashrate.insert_mhs_real(data);
|
||||||
|
} else {
|
||||||
|
await hashrate.insert_mhs_real(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main()
|
Loading…
Reference in New Issue