m2pool_backend_app/lib/node.js

518 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
for(let item of fixedOutputs){
const {attoAlphAmount, address } = item
if(address === REPORT_ADDRESS){
return {height, hash: block_hash, time:Math.trunc(timestamp / 1000), block_reward: Number(attoAlphAmount), block_fees: null };
}
}
// 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
};