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.

typescript
getEpochTemplate() signature
async getEpochTemplate(): Promise<EpochTemplate>

EpochTemplate Structure

typescript
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.

typescript
Basic template query
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.

typescript
Monitor for new template
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.

typescript
Calculate mining hashrate
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.

typescript
Track mining performance
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.

typescript
Get current difficulty
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.

typescript
Track difficulty changes
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}`);
}