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 };