feat(mining): Ported mining endpoints from bch-api

This commit is contained in:
Chris Troutner
2025-11-14 05:22:59 -08:00
parent f2e61fde35
commit 4f89335768
8 changed files with 416 additions and 0 deletions
@@ -0,0 +1,99 @@
/*
REST API Controller for the /full-node/mining routes.
*/
import wlogger from '../../../../adapters/wlogger.js'
class MiningRESTController {
constructor (localConfig = {}) {
this.adapters = localConfig.adapters
if (!this.adapters) {
throw new Error(
'Instance of Adapters library required when instantiating Mining REST Controller.'
)
}
this.useCases = localConfig.useCases
if (!this.useCases || !this.useCases.mining) {
throw new Error(
'Instance of Mining use cases required when instantiating Mining REST Controller.'
)
}
this.miningUseCases = this.useCases.mining
// Bind functions
this.root = this.root.bind(this)
this.getMiningInfo = this.getMiningInfo.bind(this)
this.getNetworkHashPS = this.getNetworkHashPS.bind(this)
this.handleError = this.handleError.bind(this)
}
/**
* @api {get} /v6/full-node/mining/ Service status
* @apiName MiningRoot
* @apiGroup Mining
*
* @apiDescription Returns the status of the mining service.
*
* @apiSuccess {String} status Service identifier
*/
async root (req, res) {
return res.status(200).json({ status: 'mining' })
}
/**
* @api {get} /v6/full-node/mining/getMiningInfo Get Mining Info
* @apiName GetMiningInfo
* @apiGroup Mining
* @apiDescription Returns a json object containing mining-related information.
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v6/full-node/mining/getMiningInfo" -H "accept: application/json"
*/
async getMiningInfo (req, res) {
try {
const result = await this.miningUseCases.getMiningInfo()
return res.status(200).json(result)
} catch (err) {
return this.handleError(err, res)
}
}
/**
* @api {get} /v6/full-node/mining/getNetworkHashPS Get Estimated network hashes per second
* @apiName GetNetworkHashPS
* @apiGroup Mining
* @apiDescription Returns the estimated network hashes per second based on the last n blocks. Pass in [nblocks] to override # of blocks, -1 specifies since last difficulty change. Pass in [height] to estimate the network speed at the time when a certain block was found.
*
* @apiParam {Number} nblocks Number of blocks to use for estimation (default: 120)
* @apiParam {Number} height Block height to estimate at (default: -1)
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v6/full-node/mining/getNetworkHashPS?nblocks=120&height=-1" -H "accept: application/json"
*/
async getNetworkHashPS (req, res) {
try {
let nblocks = 120 // Default
let height = -1 // Default
if (req.query.nblocks) nblocks = parseInt(req.query.nblocks)
if (req.query.height) height = parseInt(req.query.height)
const result = await this.miningUseCases.getNetworkHashPS({ nblocks, height })
return res.status(200).json(result)
} catch (err) {
return this.handleError(err, res)
}
}
handleError (err, res) {
wlogger.error('Error in MiningRESTController:', err)
const status = err.status || 500
const message = err.message || 'Internal server error'
return res.status(status).json({ error: message })
}
}
export default MiningRESTController
@@ -0,0 +1,52 @@
/*
REST API router for /full-node/mining routes.
*/
import express from 'express'
import MiningRESTController from './controller.js'
class MiningRouter {
constructor (localConfig = {}) {
this.adapters = localConfig.adapters
if (!this.adapters) {
throw new Error(
'Instance of Adapters library required when instantiating Mining REST Router.'
)
}
this.useCases = localConfig.useCases
if (!this.useCases) {
throw new Error(
'Instance of Use Cases library required when instantiating Mining REST Router.'
)
}
const dependencies = {
adapters: this.adapters,
useCases: this.useCases
}
this.miningController = new MiningRESTController(dependencies)
this.apiPrefix = (localConfig.apiPrefix || '').replace(/\/$/, '')
this.baseUrl = `${this.apiPrefix}/full-node/mining`
if (!this.baseUrl.startsWith('/')) {
this.baseUrl = `/${this.baseUrl}`
}
this.router = express.Router()
}
attach (app) {
if (!app) {
throw new Error('Must pass app object when attaching REST API controllers.')
}
this.router.get('/', this.miningController.root)
this.router.get('/getMiningInfo', this.miningController.getMiningInfo)
this.router.get('/getNetworkHashPS', this.miningController.getNetworkHashPS)
app.use(this.baseUrl, this.router)
}
}
export default MiningRouter
+4
View File
@@ -10,6 +10,7 @@
import BlockchainRouter from './full-node/blockchain/index.js'
import ControlRouter from './full-node/control/index.js'
import DSProofRouter from './full-node/dsproof/index.js'
import MiningRouter from './full-node/mining/index.js'
import config from '../../config/index.js'
class RESTControllers {
@@ -64,6 +65,9 @@ class RESTControllers {
const dsproofRouter = new DSProofRouter(dependencies)
dsproofRouter.attach(app)
const miningRouter = new MiningRouter(dependencies)
miningRouter.attach(app)
}
}
@@ -0,0 +1,28 @@
/*
Use cases for interacting with the BCH full node mining RPC interface.
*/
class MiningUseCases {
constructor (localConfig = {}) {
this.adapters = localConfig.adapters
if (!this.adapters) {
throw new Error('Adapters instance required when instantiating Mining use cases.')
}
this.fullNode = this.adapters.fullNode
if (!this.fullNode) {
throw new Error('Full node adapter required when instantiating Mining use cases.')
}
}
async getMiningInfo () {
return this.fullNode.call('getmininginfo')
}
async getNetworkHashPS ({ nblocks, height }) {
return this.fullNode.call('getnetworkhashps', [nblocks, height])
}
}
export default MiningUseCases
+2
View File
@@ -8,6 +8,7 @@
import BlockchainUseCases from './full-node-blockchain-use-cases.js'
import ControlUseCases from './full-node-control-use-cases.js'
import DSProofUseCases from './full-node-dsproof-use-cases.js'
import MiningUseCases from './full-node-mining-use-cases.js'
class UseCases {
constructor (localConfig = {}) {
@@ -21,6 +22,7 @@ class UseCases {
this.blockchain = new BlockchainUseCases({ adapters: this.adapters })
this.control = new ControlUseCases({ adapters: this.adapters })
this.dsproof = new DSProofUseCases({ adapters: this.adapters })
this.mining = new MiningUseCases({ adapters: this.adapters })
}
// Run any startup Use Cases at the start of the app.