m2pool_backend_app/lib/node.js

518 lines
13 KiB
JavaScript
Raw Permalink Normal View History

2025-04-10 10:53:16 +00:00
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
2025-04-11 11:59:58 +00:00
for(let item of fixedOutputs){
const {attoAlphAmount, address } = item
2025-04-10 10:53:16 +00:00
if(address === REPORT_ADDRESS){
2025-04-15 03:15:38 +00:00
return {height, hash: block_hash, time:Math.trunc(timestamp / 1000), block_reward: BigInt(attoAlphAmount), block_fees: null };
2025-04-10 10:53:16 +00:00
}
}
2025-04-11 11:59:58 +00:00
// 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 };
// }
// }
2025-04-10 10:53:16 +00:00
}
return false
} catch(err){
throw err
}
}
block(data){
return data
}
}
module.exports = {
NEXARPCNode,
GRSRPCNode,
MONARPCNode,
DGBRPCNode,
RXDRPCNode,
ENXNode,
ALPHRPCNode
};