Mining Template
Overview
The epoch template provides the target hash and difficulty parameters required to mine the current epoch. Miners use this template to compute SHA-1 collision solutions that meet the network's difficulty requirements. The provider exposes the getEpochTemplate() method to retrieve the current mining target, which should be polled regularly as templates change when new epochs begin.
Fetching the Mining Template
Using getEpochTemplate()
The getEpochTemplate() method retrieves the current epoch's mining template from the network. This method takes no parameters and returns the active template containing the target hash and epoch number required for mining.
The returned EpochTemplate object provides the information needed to compute valid SHA-1 collision solutions. Miners should poll this method regularly to detect epoch changes and ensure they are mining against the current target.
async getEpochTemplate(): Promise<EpochTemplate>EpochTemplate Structure
interface EpochTemplate {
epochNumber: bigint; // Current epoch being mined
epochTarget: Uint8Array; // Target hash for collision
}Basic Template Query
The following example demonstrates retrieving the current mining template. The template provides the epoch number and target hash, which are the essential inputs for computing a valid SHA-1 collision solution.
import { JSONRpcProvider } from 'opnet';
import { networks, toHex } from '@btc-vision/bitcoin';
const network = networks.regtest;
const provider = new JSONRpcProvider({ url: 'https://regtest.opnet.org', network });
const template = await provider.getEpochTemplate();
console.log('Mining Template:');
console.log(' Epoch:', template.epochNumber);
console.log(' Target:', toHex(template.epochTarget));Watch for New Epochs
Continuous mining operations should monitor for template changes to detect when a new epoch begins. The following example polls for templates at regular intervals and invokes a callback when the epoch number changes, enabling miners to restart their search with the new target.
import { toHex } from '@btc-vision/bitcoin';
async function monitorTemplates(
provider: JSONRpcProvider,
onNewEpoch: (template: EpochTemplate) => void,
intervalMs: number = 10000
): Promise<() => void> {
let lastEpoch = -1n;
const intervalId = setInterval(async () => {
try {
const template = await provider.getEpochTemplate();
if (template.epochNumber !== lastEpoch) {
lastEpoch = template.epochNumber;
onNewEpoch(template);
}
} catch (error) {
console.error('Error fetching template:', error);
}
}, intervalMs);
return () => clearInterval(intervalId);
}
// Usage
const stopMonitoring = await monitorTemplates(provider, (template) => {
console.log('New epoch started:', template.epochNumber);
console.log('New target:', toHex(template.epochTarget));
});Mining Process
Mining requires finding a SHA-1 collision against the target hash. The algorithm follows these steps:
- Compute the preimage by XORing the checksum root, ML-DSA public key, and a 32-byte salt.
- Calculate the SHA-1 hash of the preimage.
- Count matching leading bits between the hash and the SHA-1 hash of the checksum root.
- The solution is valid if the matching bits meet or exceed the minimum difficulty.
For information on submitting valid solutions, refer to the Submission Workflow in the Submitting Epochs section.
Hashrate Estimation
Tracking hashrate over time provides visibility into mining performance and helps identify fluctuations or bottlenecks. The following example implements a hashrate tracker that accumulates hash counts, records periodic samples, and calculates a rolling average with human-readable formatting.
class HashrateTracker {
private hashCount: number = 0;
private startTime: number = Date.now();
private samples: number[] = [];
addHashes(count: number): void {
this.hashCount += count;
}
getHashrate(): number {
const elapsedSeconds = (Date.now() - this.startTime) / 1000;
if (elapsedSeconds === 0) return 0;
return this.hashCount / elapsedSeconds;
}
recordSample(): void {
const hashrate = this.getHashrate();
this.samples.push(hashrate);
// Keep last 60 samples
if (this.samples.length > 60) {
this.samples.shift();
}
// Reset counter
this.hashCount = 0;
this.startTime = Date.now();
}
getAverageHashrate(): number {
if (this.samples.length === 0) return 0;
const sum = this.samples.reduce((a, b) => a + b, 0);
return sum / this.samples.length;
}
formatHashrate(hashrate: number): string {
if (hashrate >= 1e9) {
return `${(hashrate / 1e9).toFixed(2)} GH/s`;
} else if (hashrate >= 1e6) {
return `${(hashrate / 1e6).toFixed(2)} MH/s`;
} else if (hashrate >= 1e3) {
return `${(hashrate / 1e3).toFixed(2)} KH/s`;
}
return `${hashrate.toFixed(2)} H/s`;
}
}
// Usage
const tracker = new HashrateTracker();
// In mining loop
tracker.addHashes(10000);
// Every second
setInterval(() => {
tracker.recordSample();
console.log('Hashrate:', tracker.formatHashrate(tracker.getAverageHashrate()));
}, 1000);Mining Statistics
Comprehensive mining statistics help evaluate long-term performance and efficiency. The following example implements a statistics tracker that records total hashes computed, solutions found, unique epochs mined, and calculates the average hashes required per solution.
interface MiningStats {
totalHashes: bigint;
solutionsFound: number;
epochsMined: Set<bigint>
startTime: number;
lastSolutionTime?: number;
}
class MiningStatsTracker {
private stats: MiningStats = {
totalHashes: 0n,
solutionsFound: 0,
epochsMined: new Set(),
startTime: Date.now(),
};
addHashes(count: bigint): void {
this.stats.totalHashes += count;
}
recordSolution(epochNumber: bigint): void {
this.stats.solutionsFound++;
this.stats.epochsMined.add(epochNumber);
this.stats.lastSolutionTime = Date.now();
}
getStats(): {
totalHashes: string;
solutions: number;
uniqueEpochs: number;
uptimeHours: number;
avgHashesPerSolution: string;
} {
const uptimeMs = Date.now() - this.stats.startTime;
const uptimeHours = uptimeMs / (1000 * 60 * 60);
const avgHashes = this.stats.solutionsFound > 0
? this.stats.totalHashes / BigInt(this.stats.solutionsFound)
: 0n;
return {
totalHashes: this.stats.totalHashes.toString(),
solutions: this.stats.solutionsFound,
uniqueEpochs: this.stats.epochsMined.size,
uptimeHours: Math.round(uptimeHours * 100) / 100,
avgHashesPerSolution: avgHashes.toString(),
};
}
}
// Usage
const statsTracker = new MiningStatsTracker();
// During mining
statsTracker.addHashes(10000n);
// On solution found
statsTracker.recordSolution(100n);
// Display stats
const stats = statsTracker.getStats();
console.log('Mining Stats:');
console.log(' Total hashes:', stats.totalHashes);
console.log(' Solutions found:', stats.solutions);
console.log(' Uptime:', stats.uptimeHours, 'hours');Difficulty Management
Get Current Difficulty
The current mining difficulty can be obtained from the latest epoch using getLatestEpoch(). The difficultyScaled and minDifficulty fields indicate the required number of matching bits for a valid solution. Monitoring difficulty over time helps miners estimate solution probability and adjust computational resources accordingly.
async function getCurrentDifficulty(
provider: JSONRpcProvider
): Promise<{
epochNumber: bigint;
difficultyScaled: bigint;
minDifficulty?: string;
}> {
const epoch = await provider.getLatestEpoch(false);
return {
epochNumber: epoch.epochNumber,
difficultyScaled: epoch.difficultyScaled,
minDifficulty: epoch.minDifficulty,
};
}
// Usage
const difficulty = await getCurrentDifficulty(provider);
console.log('Current difficulty:', difficulty.difficultyScaled);Track Difficulty Changes
Analyzing difficulty trends across epochs helps miners anticipate changes and optimize their strategies. The following example collects difficulty values from recent epochs, enabling visualization of network difficulty adjustments over time.
async function trackDifficultyHistory(
provider: JSONRpcProvider,
epochCount: number
): Promise<Array<{ epoch: bigint; difficulty: bigint }>> {
const history: Array<{ epoch: bigint; difficulty: bigint }> = [];
const latest = await provider.getLatestEpoch(false);
for (let i = 0; i < epochCount; i++) {
const epochNum = latest.epochNumber - BigInt(i);
if (epochNum < 0n) break;
const epoch = await provider.getEpochByNumber(epochNum);
history.push({
epoch: epoch.epochNumber,
difficulty: epoch.difficultyScaled,
});
}
return history.reverse();
}
// Usage
const difficultyHistory = await trackDifficultyHistory(provider, 20);
console.log('Difficulty over last 20 epochs:');
for (const entry of difficultyHistory) {
console.log(` Epoch ${entry.epoch}: ${entry.difficulty}`);
}