518 lines
13 KiB
JavaScript
518 lines
13 KiB
JavaScript
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: BigInt(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
|
||
};
|