@btc-vision/transaction
Version:
OPNet transaction library allows you to create and sign transactions for the OPNet network.
463 lines (340 loc) • 13.9 kB
Markdown
# ChallengeSolution
Represents and validates epoch mining challenge solutions in the OPNet proof-of-work system.
## Overview
The epoch mining system in OPNet divides blockchain time into epochs of 5 blocks each. Miners compete to find solutions that match a target hash with a certain number of leading matching bits (the difficulty). A `ChallengeSolution` encapsulates all the data needed to represent, validate, and submit a mining solution: the epoch number, the miner's public key, the cryptographic solution, a salt, optional graffiti, the difficulty level, and verification proofs.
The module exports three classes:
- **`ChallengeSolution`** -- The main class holding the complete challenge solution.
- **`ChallengeVerification`** -- Epoch verification data (hashes, block range, Merkle proofs).
- **`ChallengeSubmission`** -- A signed submission of a solution for a future epoch.
**Source:** `src/epoch/ChallengeSolution.ts`
## Table of Contents
- [Import](#import)
- [Interfaces and Types](#interfaces-and-types)
- [IChallengeSolution](#ichallengesolution)
- [IChallengeVerification](#ichallengeverification)
- [IChallengeSubmission](#ichallengesubmission)
- [RawChallenge](#rawchallenge)
- [RawChallengeVerification](#rawchallengeverification)
- [RawChallengeSubmission](#rawchallengesubmission)
- [ChallengeVerification Class](#challengeverification-class)
- [ChallengeSubmission Class](#challengesubmission-class)
- [ChallengeSolution Class](#challengesolution-class)
- [Constructor](#constructor)
- [Properties](#properties)
- [Static Methods](#static-methods)
- [Instance Methods](#instance-methods)
- [How Epoch Mining Works](#how-epoch-mining-works)
- [Examples](#examples)
- [Related Documentation](#related-documentation)
---
## Import
```typescript
import {
ChallengeSolution,
ChallengeVerification,
ChallengeSubmission,
} from '@btc-vision/transaction';
```
---
## Interfaces and Types
### IChallengeSolution
The full interface for a challenge solution.
```typescript
interface IChallengeSolution {
readonly epochNumber: bigint;
readonly publicKey: Address;
readonly solution: Uint8Array;
readonly salt: Uint8Array;
readonly graffiti: Uint8Array;
readonly difficulty: number;
readonly verification: IChallengeVerification;
verifySubmissionSignature(): boolean;
getSubmission(): IChallengeSubmission | undefined;
toRaw(): RawChallenge;
verify(): boolean;
toBuffer(): Uint8Array;
toHex(): string;
calculateSolution(): Uint8Array;
checkDifficulty(minDifficulty: number): { valid: boolean; difficulty: number };
getMiningTargetBlock(): bigint | null;
}
```
### IChallengeVerification
Verification data proving the epoch state used for the challenge.
```typescript
interface IChallengeVerification {
readonly epochHash: Uint8Array;
readonly epochRoot: Uint8Array;
readonly targetHash: Uint8Array;
readonly targetChecksum: Uint8Array;
readonly startBlock: bigint;
readonly endBlock: bigint;
readonly proofs: readonly Uint8Array[];
}
```
| Property | Type | Description |
|----------|------|-------------|
| `epochHash` | `Uint8Array` | The hash of the epoch data. |
| `epochRoot` | `Uint8Array` | The Merkle root of the epoch. |
| `targetHash` | `Uint8Array` | The hash that solutions are compared against for difficulty. |
| `targetChecksum` | `Uint8Array` | The checksum used in the XOR preimage calculation (32 bytes). |
| `startBlock` | `bigint` | The first block number in this epoch. |
| `endBlock` | `bigint` | The last block number in this epoch. |
| `proofs` | `readonly Uint8Array[]` | Merkle proofs for epoch verification. |
### IChallengeSubmission
A signed submission of a challenge solution.
```typescript
interface IChallengeSubmission {
readonly publicKey: Address;
readonly solution: Uint8Array;
readonly graffiti: Uint8Array | undefined;
readonly signature: Uint8Array;
readonly epochNumber: bigint;
verifySignature(): boolean;
}
```
### RawChallenge
Serializable representation of a challenge solution (all values as strings).
```typescript
interface RawChallenge {
readonly epochNumber: string;
readonly mldsaPublicKey: string;
readonly legacyPublicKey: string;
readonly solution: string;
readonly salt: string;
readonly graffiti: string;
readonly difficulty: number;
readonly verification: RawChallengeVerification;
readonly submission?: RawChallengeSubmission;
}
```
### RawChallengeVerification
```typescript
interface RawChallengeVerification {
readonly epochHash: string;
readonly epochRoot: string;
readonly targetHash: string;
readonly targetChecksum: string;
readonly startBlock: string;
readonly endBlock: string;
readonly proofs: readonly string[];
}
```
### RawChallengeSubmission
```typescript
interface RawChallengeSubmission {
readonly mldsaPublicKey: string;
readonly legacyPublicKey: string;
readonly solution: string;
readonly graffiti?: string;
readonly signature: string;
}
```
---
## ChallengeVerification Class
Parses and stores epoch verification data from raw string format.
```typescript
class ChallengeVerification implements IChallengeVerification {
constructor(data: RawChallengeVerification)
}
```
The constructor converts all hex strings to `Uint8Array` and all numeric strings to `bigint`.
| Property | Type | Description |
|----------|------|-------------|
| `epochHash` | `Uint8Array` | Parsed epoch hash. |
| `epochRoot` | `Uint8Array` | Parsed epoch Merkle root. |
| `targetHash` | `Uint8Array` | Parsed target hash for difficulty comparison. |
| `targetChecksum` | `Uint8Array` | Parsed target checksum for preimage XOR. |
| `startBlock` | `bigint` | First block of the epoch. |
| `endBlock` | `bigint` | Last block of the epoch. |
| `proofs` | `readonly Uint8Array[]` | Frozen array of parsed Merkle proof elements. |
---
## ChallengeSubmission Class
Represents a signed submission for a future epoch (typically `epochNumber + 2`).
```typescript
class ChallengeSubmission implements IChallengeSubmission {
constructor(data: RawChallengeSubmission, epochNumber: bigint)
}
```
| Property | Type | Description |
|----------|------|-------------|
| `publicKey` | `Address` | The submitter's address (MLDSA + legacy keys). |
| `solution` | `Uint8Array` | The submitted solution bytes. |
| `graffiti` | `Uint8Array \| undefined` | Optional graffiti data. |
| `signature` | `Uint8Array` | The Schnorr signature over the submission data. |
| `epochNumber` | `bigint` | The epoch this submission targets. |
### verifySignature
```typescript
public verifySignature(): boolean
```
Verifies the Schnorr signature over `[publicKey, epochNumber, solution, graffiti?]`. Returns `true` if the signature is valid.
---
## ChallengeSolution Class
The main class that encapsulates a complete epoch mining challenge solution.
### Constructor
```typescript
const challenge = new ChallengeSolution(data: RawChallenge);
```
Parses all fields from the raw string-based format. If the raw data includes a `submission`, it creates a `ChallengeSubmission` for epoch `epochNumber + 2`.
### Properties
| Property | Type | Description |
|----------|------|-------------|
| `epochNumber` | `bigint` | The epoch number this solution is for. |
| `publicKey` | `Address` | The miner's address (MLDSA + legacy public keys). |
| `solution` | `Uint8Array` | The SHA-1 hash of the preimage (the "solution"). |
| `salt` | `Uint8Array` | 32-byte random salt used in the preimage. |
| `graffiti` | `Uint8Array` | Arbitrary data embedded by the miner (like a coinbase message). |
| `difficulty` | `number` | Number of matching leading bits between solution and target hash. |
| `verification` | `ChallengeVerification` | Epoch verification data (hashes, block range, proofs). |
### Static Methods
#### validateRaw
```typescript
static validateRaw(data: RawChallenge): boolean
```
Validates a challenge solution directly from raw string data without constructing a full `ChallengeSolution` instance. Delegates to `EpochValidator.validateEpochWinner()`.
| Parameter | Type | Description |
|-----------|------|-------------|
| `data` | `RawChallenge` | The raw challenge data to validate. |
**Returns:** `boolean` -- `true` if the solution is valid.
### Instance Methods
#### verify
```typescript
public verify(): boolean
```
Validates this challenge solution by verifying the preimage calculation, SHA-1 hash, difficulty match, and block range. Delegates to `EpochValidator.validateChallengeSolution()`.
**Returns:** `boolean` -- `true` if the challenge is valid.
#### getSubmission
```typescript
public getSubmission(): ChallengeSubmission | undefined
```
Returns the challenge submission if present and its signature is valid.
**Returns:** `ChallengeSubmission | undefined` -- The verified submission, or `undefined` if no submission is present.
**Throws:** `Error('Invalid submission signature.')` if the submission exists but its signature verification fails.
#### verifySubmissionSignature
```typescript
public verifySubmissionSignature(): boolean
```
Verifies the Schnorr signature on the embedded submission.
**Returns:** `boolean`
**Throws:** `Error` if no submission is present.
#### toBuffer
```typescript
public toBuffer(): Uint8Array
```
Returns the solution bytes as a `Uint8Array`.
#### toHex
```typescript
public toHex(): string
```
Returns the solution as a `0x`-prefixed hex string.
#### toRaw
```typescript
public toRaw(): RawChallenge
```
Serializes the challenge back to the raw string format suitable for JSON transmission.
#### calculateSolution
```typescript
public calculateSolution(): Uint8Array
```
Recalculates the expected solution hash from the preimage components (`targetChecksum XOR publicKey XOR salt`, then SHA-1). Useful for verifying that the stored solution matches the calculated value.
**Returns:** `Uint8Array` -- The SHA-1 hash of the preimage.
#### checkDifficulty
```typescript
public checkDifficulty(minDifficulty: number): { valid: boolean; difficulty: number }
```
Checks whether the solution meets a minimum difficulty requirement.
| Parameter | Type | Description |
|-----------|------|-------------|
| `minDifficulty` | `number` | The minimum number of matching leading bits required. |
**Returns:** `{ valid: boolean; difficulty: number }` -- Whether the difficulty is met, and the actual difficulty.
#### getMiningTargetBlock
```typescript
public getMiningTargetBlock(): bigint | null
```
Returns the block number that miners should target for this epoch. For epoch 0, returns `null` (cannot be mined). For other epochs, returns `epochNumber * 5 - 1` (the last block of the previous epoch).
**Returns:** `bigint | null`
---
## How Epoch Mining Works
OPNet divides blockchain time into **epochs** of 5 blocks each:
```
Epoch 0: blocks 0-4
Epoch 1: blocks 5-9
Epoch 2: blocks 10-14
...
Epoch N: blocks (N*5) to (N*5 + 4)
```
**Mining process:**
1. At the end of each epoch, a `targetChecksum` and `targetHash` are derived from the epoch's state.
2. Miners attempt to find a **salt** such that:
```
preimage = targetChecksum XOR publicKey XOR salt (byte-by-byte, 32 bytes)
solution = SHA1(preimage) (20 bytes)
difficulty = countMatchingLeadingBits(solution, targetHash)
```
3. The miner with the highest `difficulty` (most matching leading bits) wins the epoch reward.
4. Solutions can be submitted for a future epoch (`epochNumber + 2`) via the `ChallengeSubmission` mechanism, which includes a Schnorr signature to prove authorship.
---
## Examples
### Validating a Challenge from Raw Data
```typescript
import { ChallengeSolution } from '@btc-vision/transaction';
const rawData: RawChallenge = {
epochNumber: '42',
mldsaPublicKey: '0xabcd...',
legacyPublicKey: '0x02abcd...',
solution: '0x1234...',
salt: '0x5678...',
graffiti: '0xdeadbeef...',
difficulty: 18,
verification: {
epochHash: '0x...',
epochRoot: '0x...',
targetHash: '0x...',
targetChecksum: '0x...',
startBlock: '210',
endBlock: '214',
proofs: ['0x...', '0x...'],
},
};
// Quick validation without full object construction
const isValid = ChallengeSolution.validateRaw(rawData);
console.log(`Valid: ${isValid}`);
// Full object with methods
const challenge = new ChallengeSolution(rawData);
console.log(`Epoch: ${challenge.epochNumber}`);
console.log(`Difficulty: ${challenge.difficulty}`);
console.log(`Valid: ${challenge.verify()}`);
console.log(`Solution hex: ${challenge.toHex()}`);
```
### Checking Difficulty Requirements
```typescript
const challenge = new ChallengeSolution(rawData);
const result = challenge.checkDifficulty(16);
if (result.valid) {
console.log(`Solution meets minimum difficulty: ${result.difficulty} bits`);
} else {
console.log(`Insufficient difficulty: ${result.difficulty} < 16`);
}
```
### Getting Mining Target Block
```typescript
const challenge = new ChallengeSolution(rawData);
const targetBlock = challenge.getMiningTargetBlock();
if (targetBlock !== null) {
console.log(`Mine at block: ${targetBlock}`);
} else {
console.log('Epoch 0 cannot be mined');
}
```
### Serializing for Transmission
```typescript
const challenge = new ChallengeSolution(rawData);
const raw = challenge.toRaw();
const json = JSON.stringify(raw);
// Send over the network...
```
---
## Related Documentation
- [EpochValidator](./epoch-validator.md) -- Low-level epoch validation and proof verification
- [Generators](../generators/generators.md) -- Script generators that embed challenge data in transactions
- [Transaction Factory](../transaction-building/transaction-factory.md) -- How challenge solutions are included in transactions