Witness Miner
Help build the future of decentralized storage! The DIG Network is an open-source project that needs community support to continue development.
π Support the Project β - Donate crypto, buy NFTs, or sponsor development
Overviewβ
Witness Miners validate DIG nodes by verifying they are online and actively serving content. Through proof-of-work based assignment and proof validation, witnesses earn rewards while ensuring network integrity. Like PlotCoins, WitnessCoins require staking DIG tokens to participate in the network.
Typical Deploymentβ
Most witness miners run as supplemental processes alongside DIG nodes to maximize reward opportunities. Both node types require staking DIG tokens:
DIG Node Server:
βββ DIG Node Process (primary) - requires stake for PlotCoins
βββ Witness Miner Process (supplemental) - requires stake for WitnessCoins
Proof-of-Work Foundationβ
The DIG Network's witness mining system builds upon proven proof-of-work principles to create a fair, decentralized validation mechanism:
Core Principlesβ
Computational Fairness: Every witness miner must perform computational work to earn validation assignments, preventing arbitrary target selection and ensuring equitable distribution of validation opportunities across the network.
Cryptographic Binding: Each miner's work is cryptographically bound to both the current blockchain state (via Chia block hashes) and their unique identity (public key), creating tamper-proof assignment proofs while maintaining network-wide coordination.
Economic Security: The combination of computational cost and staking requirements creates multiple layers of sybil resistance, making it economically unfeasible to attack the validation system while ensuring committed participation.
Network Protection Mechanismsβ
Rate Limiting Through Difficulty:
βββ Mining Difficulty: 3-12 hex characters (adjustable)
βββ Block Intervals: Every 38th Chia block (~10 minutes)
βββ Success Rate: ~10% find assignment per period
βββ Network Load: 1-2 validations per 10 minutes globally
DDOS Protection Through Economics:
βββ Computational Cost: 1000+ hashes per attempt
βββ Time Investment: 0-10 minutes per assignment
βββ Stake Requirement: TBD DIG tokens locked
βββ Single Assignment: One validation per valid block
Distributed Consensus Propertiesβ
The proof-of-work assignment system provides key distributed consensus guarantees:
- Permissionless Participation: Any entity with stake can join without approval
- Verifiable Randomness: All assignments can be independently verified
- Collusion Resistance: Miners cannot choose their validation targets
- Network Coverage: Assignments naturally spread across all DIG nodes
- Difficulty Adjustment: Self-regulating system maintains optimal load
This foundation ensures the DIG Network maintains decentralized validation while protecting storage providers from overwhelming validation requests.
Proposed Evolution: Dual Farming Architectureβ
In potential future collaboration with Chia community experts, the system could theoretically evolve to leverage Chia's Proof of Space and Time consensus mechanism. This hypothetical integration would enable DIG Nodes to "dual farm" their existing Chia farming plots, conceptually allowing simultaneous:
- Farming Chia for blockchain consensus rewards
- Proving DIG storage for data persistence verification
- Farming Peer Assignments for Witness Miners
This proposed dual-purpose approach would maximize infrastructure efficiency by repurposing existing Chia farming hardware to provide cryptographic proof of unique data storage, creating a potential symbiotic relationship between Chia's consensus layer and DIG's storage verification protocol.
The envisioned result would be an economically efficient system where witness "farmers" could monetize the same physical infrastructure across both networks while providing mathematically validation assignments and availabilityβpending technical feasibility and community collaboration.
System Requirementsβ
Economic Requirementsβ
DIG Stake: TBD (locked for 7 days)
Wallet: Chia wallet with DIG tokens
Core Architectureβ
class WitnessNode:
# Validation Engine
entropy_generator: EntropyGenerator # Block hash entropy management
target_selector: TargetSelector # Proof-of-work based target selection
proof_validator: ProofValidator # Verify physical access proofs
difficulty_controller: NetworkDifficultyController # Manage PoW difficulty
# Blockchain Interface
chia_client: ChiaClient # Monitor block hashes
witness_coin_creator: WitnessCoinCreator # Create on-chain proofs
plotcoin_registry: PlotCoinRegistry # Track available targets
# Network Communication
dig_client: DIGNodeClient # Request proofs from DIG nodes
rate_limiter: RateLimiter # Prevent DDOS behavior
# Economic Tracking
stake_manager: WitnessStakeManager # Manage witness stake
reward_tracker: RewardTracker # Monitor earnings
epoch_manager: EpochManager # Track validation periods
# Proof-of-Work Mining
mining_engine: MiningEngine # PoW computation engine
assignment_cache: AssignmentCache # Cache valid assignments
block_validator: BlockValidator # Verify 38-block intervals
Validation Protocolβ
The witness validation protocol uses a proof-of-work based assignment system that ensures permissionless participation, fair distribution, and DDOS protection:
- Block Synchronization: Every 38th Chia block (approximately every 10 minutes) serves as a synchronization point
- Unique Entropy: Each witness creates a unique seed by combining the block hash with their public key
- Mining for Assignment: Witnesses perform proof-of-work to find a plotcoin assignment matching network difficulty
- Rate Limiting: One validation per valid block prevents overwhelming the network
- Verifiable Fairness: All assignments can be cryptographically verified on-chain
Proof-of-Work Based Assignmentβ
class EntropyGenerator:
BLOCK_INTERVAL = 38 # Only every 38th block is valid
def get_valid_entropy_seed(self):
current_height = self.chia_client.get_block_height()
# Find nearest valid block (divisible by 38)
valid_height = (current_height // self.BLOCK_INTERVAL) * self.BLOCK_INTERVAL
# Get block hash at valid height
block_hash = self.chia_client.get_block_hash(valid_height)
# Create seed commitment: Block Hash + Public Key
# This ensures unique assignments per witness while binding to same block
seed_commitment = sha256(block_hash + self.public_key).digest()
return {
"block_height": valid_height,
"block_hash": block_hash,
"seed_commitment": seed_commitment,
"valid_until": valid_height + self.BLOCK_INTERVAL,
"mining_window": self.BLOCK_INTERVAL * 16 # seconds until next valid block
}
def is_valid_block(self, block_height):
"""Check if a block height is valid for entropy generation"""
return block_height % self.BLOCK_INTERVAL == 0
Proof-of-Work Target Selectionβ
class TargetSelector:
def __init__(self, plotcoin_registry, difficulty_controller):
self.plotcoin_registry = plotcoin_registry
self.difficulty_controller = difficulty_controller
self.plotcoin_hashmap = {}
self.prefix_to_plotcoins = {}
self.current_difficulty = 6 # Starting difficulty (prefix length)
self.last_validation_block = None
def build_plotcoin_hashmap(self):
"""Build hash table of all plotcoins in the network"""
all_plotcoins = self.plotcoin_registry.get_active_plotcoins()
self.plotcoin_hashmap.clear()
self.prefix_to_plotcoins.clear()
for plotcoin in all_plotcoins:
# Use plotcoin ID as the hash key
plotcoin_hash = sha256(plotcoin.id).hexdigest()
self.plotcoin_hashmap[plotcoin_hash] = plotcoin
# Build prefix mapping for efficient PoW matching
prefix = plotcoin_hash[:self.current_difficulty]
if prefix not in self.prefix_to_plotcoins:
self.prefix_to_plotcoins[prefix] = []
self.prefix_to_plotcoins[prefix].append(plotcoin)
return len(self.plotcoin_hashmap)
def generate_seed_commitment(self, block_hash, public_key):
"""Generate unique entropy seed from block hash and witness public key"""
seed_data = block_hash + public_key
return sha256(seed_data).digest()
def mine_for_assignment(self, entropy_seed, max_iterations=600000):
"""
Proof-of-work mining to find valid plotcoin assignment
max_iterations = ~10 minutes at 1000 hash/sec
"""
nonce = 0
start_time = time.time()
# Update difficulty from controller
self.current_difficulty = self.difficulty_controller.get_current_difficulty()
# Rebuild hashmap if difficulty changed
if self.current_difficulty != len(next(iter(self.prefix_to_plotcoins.keys()), "")):
self.build_plotcoin_hashmap()
while nonce < max_iterations:
# Construct mining input: entropy_seed + nonce
mining_input = entropy_seed + nonce.to_bytes(8, 'big')
candidate_hash = hashlib.sha256(mining_input).hexdigest()
candidate_prefix = candidate_hash[:self.current_difficulty]
# Check if prefix matches any plotcoin
if candidate_prefix in self.prefix_to_plotcoins:
end_time = time.time()
matched_plotcoins = self.prefix_to_plotcoins[candidate_prefix]
# Select specific plotcoin deterministically from matches
selection_index = int(candidate_hash[self.current_difficulty:self.current_difficulty+2], 16) % len(matched_plotcoins)
selected_plotcoin = matched_plotcoins[selection_index]
# Record match for difficulty adjustment
self.difficulty_controller.record_match(end_time, self.witness_id)
return ValidationAssignment(
success=True,
plotcoin=selected_plotcoin,
nonce=nonce,
mining_time=end_time - start_time,
proof_of_work=ProofOfWork(
entropy_seed=entropy_seed.hex(),
nonce=nonce,
result_hash=candidate_hash,
matched_prefix=candidate_prefix,
difficulty=self.current_difficulty,
timestamp=int(end_time)
)
)
nonce += 1
# Progress update every 60 seconds
if nonce % 60000 == 0:
elapsed = time.time() - start_time
print(f"Mining attempt {nonce:,} in {elapsed:.1f}s")
return ValidationAssignment(
success=False,
message="No assignment found in time window",
attempts=max_iterations
)
def select_validation_target(self, valid_block_height, block_hash, public_key):
"""
Main entry point for validation target selection
"""
# Verify block is valid (multiple of 38)
if valid_block_height % 38 != 0:
raise ValueError(f"Invalid block height {valid_block_height}, must be multiple of 38")
# Check if already validated on this block
if self.last_validation_block == valid_block_height:
raise ValueError(f"Already validated on block {valid_block_height}")
# Generate entropy seed
entropy_seed = self.generate_seed_commitment(block_hash, public_key)
# Mine for assignment
assignment = self.mine_for_assignment(entropy_seed)
if assignment.success:
self.last_validation_block = valid_block_height
# Create validation target
target = ValidationTarget(
plotcoin_id=assignment.plotcoin.id,
dig_nodes=assignment.plotcoin.get_dig_nodes(),
datastore_id=assignment.plotcoin.datastore_id,
capsule_hash=self.select_capsule(assignment.plotcoin, entropy_seed),
proof_of_work=assignment.proof_of_work,
block_height=valid_block_height
)
return target
return None
def verify_proof_of_work(self, proof, plotcoin_id):
"""Verify that a proof-of-work is valid for the given plotcoin"""
# Reconstruct the hash
mining_input = bytes.fromhex(proof.entropy_seed) + proof.nonce.to_bytes(8, 'big')
result_hash = hashlib.sha256(mining_input).hexdigest()
# Verify hash matches
if result_hash != proof.result_hash:
return False
# Verify prefix matches
if result_hash[:proof.difficulty] != proof.matched_prefix:
return False
# Verify plotcoin assignment is valid
plotcoin_hash = sha256(plotcoin_id).hexdigest()
if plotcoin_hash[:proof.difficulty] != proof.matched_prefix:
return False
return True
Proof Request and Validationβ
class ValidationEngine:
def validate_with_pow(self, entropy_seed):
"""Main validation flow with proof-of-work assignment"""
# 1. Mine for valid assignment
assignment = self.target_selector.mine_for_assignment(entropy_seed)
if not assignment.success:
return ValidationResult(success=False, reason="no_pow_assignment")
# 2. Request proof from assigned DIG node
target = assignment.plotcoin
challenge = PhysicalAccessChallenge(
capsule_hash=target.capsule_hash,
block_hash=self.current_entropy.block_hash,
witness_id=self.witness_id,
proof_of_work=assignment.proof_of_work,
timestamp=int(time.time())
)
try:
response = self.dig_client.request_proof(
node_endpoint=target.dig_node_endpoint,
challenge=challenge,
timeout=30
)
except TimeoutError:
return ValidationResult(success=False, reason="timeout")
# 3. Verify all three proofs
validations = [
self.verify_ownership_proof(response.ownership_proof),
self.verify_inclusion_proof(response.inclusion_proof),
self.verify_physical_access_proof(response.physical_access_proof)
]
if not all(validations):
return ValidationResult(success=False, reason="invalid_proofs")
# 4. Verify proof-of-work assignment
if not self.target_selector.verify_proof_of_work(
assignment.proof_of_work,
target.plotcoin_id
):
return ValidationResult(success=False, reason="invalid_pow")
# 5. Verify block hash binding
if not self.verify_block_hash_binding(response, challenge.block_hash):
return ValidationResult(success=False, reason="invalid_binding")
# 6. Check block height recency
current_height = self.chia_client.get_block_height()
proof_height = response.bound_block_height
if current_height - proof_height > self.MAX_BLOCK_AGE:
return ValidationResult(success=False, reason="stale_proof")
return ValidationResult(
success=True,
proofs=response,
proof_of_work=assignment.proof_of_work,
validated_at=int(time.time()),
mining_time=assignment.mining_time
)
WitnessCoin Creationβ
Stake Requirementsβ
WITNESS_STAKE_REQUIREMENTS = {
"minimum_stake": "TBD", # DIG tokens required
"lockup_period": 604800, # 7 days in seconds
"slashing_percentage": "TBD", # For false validations
"unbonding_period": 259200 # 3 days
}
On-Chain Proof Submissionβ
class WitnessCoinCreator:
def __init__(self, stake_manager):
self.stake_manager = stake_manager
self.witness_puzzle_hash = self.get_witness_puzzle_hash()
async def create_witness_coin(self, validation_result, entropy_proof):
# Verify stake requirements
if not await self.stake_manager.verify_stake(self.witness_id):
raise InsufficientStakeError("Minimum stake not met")
# Prepare coin data
coin_data = {
# Validation proof
"dig_node_public_key": validation_result.dig_node_id,
"physical_access_proof": validation_result.proofs.physical_access_proof,
"ownership_proof": validation_result.proofs.ownership_proof,
"inclusion_proof": validation_result.proofs.inclusion_proof,
# Proof-of-Work assignment
"proof_of_work": {
"entropy_seed": validation_result.proof_of_work.entropy_seed,
"nonce": validation_result.proof_of_work.nonce,
"result_hash": validation_result.proof_of_work.result_hash,
"matched_prefix": validation_result.proof_of_work.matched_prefix,
"difficulty": validation_result.proof_of_work.difficulty,
"mining_time": validation_result.mining_time
},
# Block binding proof
"block_height": entropy_proof.block_height,
"block_hash": entropy_proof.block_hash,
"plotcoin_id": validation_result.plotcoin_id,
# Witness signature
"witness_id": self.witness_id,
"witness_signature": self.sign_validation(validation_result),
"timestamp": validation_result.validated_at,
# Stake proof
"stake_proof": await self.stake_manager.generate_stake_proof()
}
# Create on-chain transaction with stake
witness_coin = self.create_singleton_with_stake(
parent_coin=self.get_parent_coin(),
inner_puzzle_hash=self.witness_puzzle_hash,
amount=1, # Singleton amount
stake_amount=WITNESS_STAKE_REQUIREMENTS["minimum_stake"],
metadata=coin_data
)
# Broadcast to network
tx_id = await self.broadcast_transaction(witness_coin)
return {
"witness_coin_id": witness_coin.name(),
"transaction_id": tx_id,
"epoch": self.get_current_epoch(),
"stake_locked": WITNESS_STAKE_REQUIREMENTS["minimum_stake"]
}
Proof-of-Work Verificationβ
def verify_witness_coin_pow(self, witness_coin_data):
"""
Verify proof-of-work on-chain to ensure valid assignment
This prevents witnesses from arbitrarily choosing targets
"""
pow_data = witness_coin_data["proof_of_work"]
# 1. Verify block height is valid (multiple of 38)
if witness_coin_data["block_height"] % 38 != 0:
return False, "invalid_block_height"
# 2. Reconstruct entropy seed
expected_seed = sha256(
witness_coin_data["block_hash"] +
witness_coin_data["witness_id"]
).digest()
if expected_seed.hex() != pow_data["entropy_seed"]:
return False, "invalid_entropy_seed"
# 3. Verify proof-of-work computation
mining_input = bytes.fromhex(pow_data["entropy_seed"]) + pow_data["nonce"].to_bytes(8, 'big')
computed_hash = hashlib.sha256(mining_input).hexdigest()
if computed_hash != pow_data["result_hash"]:
return False, "invalid_pow_computation"
# 4. Verify difficulty match
if computed_hash[:pow_data["difficulty"]] != pow_data["matched_prefix"]:
return False, "difficulty_mismatch"
# 5. Verify plotcoin assignment matches
plotcoin_hash = sha256(witness_coin_data["plotcoin_id"]).hexdigest()
if plotcoin_hash[:pow_data["difficulty"]] != pow_data["matched_prefix"]:
return False, "invalid_plotcoin_assignment"
# 6. Verify witness hasn't already validated this block
if self.has_witness_validated_block(
witness_coin_data["witness_id"],
witness_coin_data["block_height"]
):
return False, "duplicate_validation"
return True, "valid_pow"
DDOS Prevention Through Proof-of-Workβ
The proof-of-work mechanism provides inherent DDOS protection:
class ProofOfWorkDDOSProtection:
"""
DDOS protection is achieved through:
1. Computational cost of finding valid assignments
2. One validation per valid block (38-block intervals)
3. Deterministic spreading across the network
4. Adjustable difficulty based on network load
"""
def __init__(self, difficulty_controller):
self.difficulty_controller = difficulty_controller
self.validation_history = {}
def estimate_protection_level(self, num_witnesses, current_difficulty):
"""Calculate expected request rate to any single DIG node"""
# Probability a specific plotcoin is selected
prob_single_plotcoin = 1 / (16 ** current_difficulty)
# Expected validations per witness per hour
validations_per_witness = 3600 / (self.BLOCK_INTERVAL * 16) # ~5.9/hour max
# Mining success rate (finding any valid assignment)
mining_success_rate = 0.1 # ~10% find assignment in time window
# Expected requests per DIG node per hour
requests_per_node = (
num_witnesses *
validations_per_witness *
mining_success_rate *
prob_single_plotcoin
)
return {
"requests_per_node_per_hour": requests_per_node,
"max_network_validations_per_hour": num_witnesses * validations_per_witness,
"difficulty": current_difficulty,
"protection_factor": 1 / prob_single_plotcoin
}
def verify_rate_compliance(self, witness_id, block_height):
"""Ensure witness follows one-validation-per-block rule"""
last_validation = self.validation_history.get(witness_id, 0)
if last_validation >= block_height:
return False, "already_validated_this_block"
# Verify block is valid (multiple of 38)
if block_height % self.BLOCK_INTERVAL != 0:
return False, "invalid_block_height"
self.validation_history[witness_id] = block_height
return True, "validation_allowed"
Economic Modelβ
Reward Distributionβ
# Run by validator at the end
class RewardModel:
# Rewards lag one epoch behind validations
def calculate_epoch_rewards(self, epoch):
# Get all WitnessCoins from previous epoch
witness_coins = self.get_witness_coins(epoch - 1)
# Calculate total rewards pool
total_pool = self.get_epoch_reward_pool(epoch)
# Split between witnesses and validated nodes
witness_share = total_pool * WITNESS_SHARE_RATE # TBD
dig_share = total_pool * DIG_SHARE_RATE # TBD
# Distribute equally among successful validations
rewards = []
for coin in witness_coins:
if coin.validation_successful:
rewards.append({
"witness": coin.witness_id,
"witness_reward": witness_share / len(witness_coins),
"dig_node": coin.dig_node_id,
"dig_reward": dig_share / len(witness_coins)
})
return rewards
Epoch Managementβ
EPOCH_BLOCKS = 37800 # ~7 days at 16s/block
class EpochManager:
def get_current_epoch(self):
current_height = self.chia_client.get_block_height()
return current_height // EPOCH_BLOCKS
def get_epoch_boundaries(self, epoch):
return {
"start_height": epoch * EPOCH_BLOCKS,
"end_height": (epoch + 1) * EPOCH_BLOCKS - 1,
"reward_distribution_height": (epoch + 1) * EPOCH_BLOCKS
}
Security Considerationsβ
Proof Verificationβ
def verify_physical_access_proof(self, proof, challenge):
# 1. Verify proof structure
required_fields = ['chunk_data', 'chunk_proof', 'timing_proof', 'nonce']
if not all(field in proof for field in required_fields):
return False
# 2. Verify timing constraint (must be generated after challenge)
if proof.timestamp <= challenge.timestamp:
return False
# 3. Verify response time (must be within window)
response_time = proof.timestamp - challenge.timestamp
if response_time > self.MAX_RESPONSE_TIME:
return False
# 4. Verify cryptographic binding
expected_hash = sha256(
proof.chunk_data +
challenge.block_hash +
proof.nonce
)
if expected_hash != proof.binding_hash:
return False
return True
Network Difficulty Controllerβ
class NetworkDifficultyController:
def __init__(self, target_matches_per_period=2, period_minutes=10):
self.target_rate = target_matches_per_period
self.period_minutes = period_minutes
self.current_difficulty = 6 # Starting difficulty (hex prefix length)
self.match_history = []
self.min_difficulty = 3
self.max_difficulty = 12
def get_current_difficulty(self):
"""Get the current network difficulty"""
return self.current_difficulty
def record_match(self, timestamp, witness_id):
"""Record when a witness finds a valid assignment"""
self.match_history.append({
'timestamp': timestamp,
'witness_id': witness_id
})
# Keep only last hour of history
cutoff = timestamp - 3600
self.match_history = [
m for m in self.match_history
if m['timestamp'] > cutoff
]
def calculate_network_rate(self):
"""Calculate current network-wide match rate"""
if len(self.match_history) < 2:
return 0
now = time.time()
period_seconds = self.period_minutes * 60
recent_matches = [
m for m in self.match_history
if m['timestamp'] > now - period_seconds
]
return len(recent_matches)
def adjust_difficulty(self):
"""Adjust difficulty based on network activity"""
current_rate = self.calculate_network_rate()
# Target range is 50% to 150% of target rate
lower_bound = self.target_rate * 0.5
upper_bound = self.target_rate * 1.5
if current_rate > upper_bound and self.current_difficulty < self.max_difficulty:
# Too many matches, increase difficulty
self.current_difficulty += 1
print(f"Increasing difficulty to {self.current_difficulty} (rate: {current_rate})")
elif current_rate < lower_bound and self.current_difficulty > self.min_difficulty:
# Too few matches, decrease difficulty
self.current_difficulty -= 1
print(f"Decreasing difficulty to {self.current_difficulty} (rate: {current_rate})")
return self.current_difficulty
def calculate_difficulty_for_network(self, num_witnesses, num_plotcoins,
target_validations_per_hour):
"""
Calculate optimal difficulty based on network parameters
Args:
num_witnesses: Active witness miners
num_plotcoins: Total plotcoins to validate
target_validations_per_hour: Desired validation rate
"""
# Assuming 1000 hashes/sec per witness
hash_rate_per_witness = 1000
total_hashes_per_hour = num_witnesses * hash_rate_per_witness * 3600
# We want: total_hashes * probability = target_validations
target_probability = target_validations_per_hour / total_hashes_per_hour
# For hex prefixes: probability β 1 / (16^prefix_length)
# Accounting for plotcoin distribution
plotcoin_factor = min(1.0, num_plotcoins / 10000) # Normalize
adjusted_probability = target_probability / plotcoin_factor
import math
optimal_difficulty = math.log(1/adjusted_probability) / math.log(16)
return max(self.min_difficulty,
min(self.max_difficulty,
int(math.ceil(optimal_difficulty))))
Proof-of-Work Benefitsβ
The proof-of-work assignment mechanism provides several critical benefits:
class ProofOfWorkMechanics:
"""
Key Benefits:
1. **Permissionless Participation**: Any witness with stake can participate
without central coordination or assignment
2. **Mathematical Distribution**: Assignments naturally spread across the
entire network based on hash prefixes
3. **Collusion Resistance**: Witnesses cannot choose which DIG nodes to
validate, preventing preferential treatment
4. **DDOS Protection**: Computational cost + rate limiting prevents
overwhelming DIG nodes with requests
5. **Verifiable Fairness**: Anyone can verify the proof-of-work to ensure
valid assignment selection
"""
def explain_assignment_process(self):
"""
Step-by-step assignment process:
1. Wait for valid block (multiple of 38)
2. Create seed: SHA256(block_hash + witness_public_key)
3. Mine: SHA256(seed + nonce) until prefix matches a plotcoin
4. Validate the matched plotcoin
5. Submit WitnessCoin with proof-of-work
6. Wait for next valid block to mine again
"""
def calculate_network_coverage(self, num_witnesses, num_plotcoins, difficulty):
"""
Estimate validation coverage across the network
"""
# Probability any specific plotcoin gets validated per period
validations_per_period = num_witnesses * 0.1 # 10% success rate
coverage_probability = validations_per_period / num_plotcoins
# Time to validate all plotcoins (probabilistic)
expected_full_coverage_hours = num_plotcoins / (validations_per_period / 6)
return {
"validations_per_hour": validations_per_period / 6,
"coverage_per_hour": coverage_probability / 6,
"expected_full_coverage_hours": expected_full_coverage_hours,
"expected_full_coverage_days": expected_full_coverage_hours / 24
}
def difficulty_adjustment_rationale(self):
"""
Difficulty adjusts to maintain balance between:
- Too Easy: DIG nodes get overwhelmed with validation requests
- Too Hard: Insufficient network validation coverage
Target: 1-2 successful validations per 10-minute period across all witnesses
"""
return {
"min_difficulty": 3, # Emergency only - very high load
"max_difficulty": 12, # Prevents validation starvation
"target_rate": "1-2 validations per 600 seconds",
"adjustment_period": "Every epoch or emergency intervention"
}
API Specificationsβ
Validation Endpointsβ
GET /witness/status
Response: {
"witness_id": "wit_abc123",
"current_epoch": 42,
"validations_this_epoch": 12,
"pending_rewards": 25.5,
"next_validation": "2024-01-01T00:10:00Z",
"stake_status": {
"amount": "TBD",
"locked_until": "2024-01-08T00:00:00Z",
"status": "active"
}
}
GET /witness/entropy
Response: {
"current_block_height": 3800000,
"valid_entropy_height": 3799962, // Latest multiple of 38
"block_hash": "0x...",
"seed_commitment": "0x...",
"next_entropy_block": 3800000,
"time_until_next": 300,
"current_difficulty": 6
}
GET /witness/mining/status
Response: {
"is_mining": true,
"current_nonce": 125000,
"attempts": 125000,
"elapsed_seconds": 125,
"hash_rate": 1000,
"entropy_seed": "0x...",
"target_difficulty": 6
}
POST /witness/validate
Body: {
"block_height": 3799962,
"block_hash": "0x..."
}
Response: {
"status": "mining_started",
"mining_job_id": "job_123",
"max_attempts": 600000,
"estimated_time": "0-10 minutes"
}
GET /witness/validation/{job_id}
Response: {
"job_id": "job_123",
"status": "completed",
"mining_result": {
"success": true,
"nonce": 234567,
"mining_time": 234.5,
"assigned_plotcoin": "plot_789",
"proof_of_work": {
"entropy_seed": "0x...",
"nonce": 234567,
"result_hash": "0x...",
"matched_prefix": "a3f",
"difficulty": 6
}
},
"validation_result": {
"success": true,
"dig_node": "dig1_xyz",
"witness_coin_id": "coin_456"
}
}
Monitoring Endpointsβ
GET /witness/history
Response: {
"validations": [
{
"timestamp": "2024-01-01T00:00:00Z",
"dig_node": "dig1_abc",
"result": "success",
"reward_epoch": 43,
"witness_coin": "coin_123"
},
...
]
}
GET /witness/targets
Response: {
"available_targets": 10000,
"validated_this_epoch": 12,
"next_target": {
"plotcoin_id": "plot_789",
"dig_nodes": ["dig1_abc", "dig1_def"],
"selection_proof": {...}
}
}
GET /witness/performance
Response: {
"witness_id": "wit_abc123",
"total_validations": 1250,
"successful_validations": 1230,
"success_rate": 0.984,
"earnings_total": 125.5,
"earnings_24h": 5.2
}
WitnessCoin Endpointsβ
GET /witness/coins/{epoch}
Response: {
"epoch": 42,
"witness_coins": [
{
"coin_id": "coin_123",
"validation_time": "2024-01-01T00:00:00Z",
"dig_node": "dig1_abc",
"status": "confirmed",
"block_height": 193600
},
...
]
}
GET /witness/coin/{coin_id}
Response: {
"coin_id": "coin_123",
"witness_id": "wit_abc123",
"dig_node_public_key": "0x...",
"plotcoin_id": "plot_789",
"validation_timestamp": 1234567890,
"proofs": {
"ownership": "valid",
"inclusion": "valid",
"physical_access": "valid"
},
"proof_of_work": {
"entropy_seed": "0x...",
"nonce": 234567,
"result_hash": "0x...",
"matched_prefix": "a3f",
"difficulty": 6,
"mining_time": 234.5,
"block_height": 3799962,
"block_hash": "0x..."
},
"transaction_id": "tx_123",
"confirmation_height": 193601
}
GET /witness/difficulty
Response: {
"current_difficulty": 6,
"network_stats": {
"active_witnesses": 1000,
"active_plotcoins": 50000,
"validations_last_hour": 120,
"target_rate_per_10min": 2
},
"adjustment_history": [
{
"timestamp": "2024-01-01T00:00:00Z",
"old_difficulty": 5,
"new_difficulty": 6,
"reason": "rate_too_high"
}
]
}
Configuration Endpointsβ
GET /witness/config
Response: {
"mode": "integrated",
"targets_per_epoch": 24,
"validation_timeout": 30,
"excluded_nodes": ["self"],
"diversity_preference": 0.8
}
POST /witness/config
Body: {
"targets_per_epoch": 30,
"validation_timeout": 45
}
Response: {
"status": "updated",
"effective_at": "next_epoch"
}
Stake Management Endpointsβ
GET /witness/stake
Response: {
"witness_id": "wit_abc123",
"stake_amount": "TBD",
"stake_status": "active",
"locked_until": "2024-01-08T00:00:00Z",
"can_withdraw": false
}
POST /witness/stake
Body: {
"amount": 150,
"wallet_id": 1
}
Response: {
"status": "staked",
"transaction_id": "tx_123",
"new_stake_amount": 150,
"locked_until": "2024-01-15T00:00:00Z"
}
POST /witness/unstake
Body: {
"amount": 100
}
Response: {
"status": "unbonding_initiated",
"available_at": "2024-01-11T00:00:00Z",
"remaining_stake": 50
}
Deploymentβ
Standalone Configurationβ
# witness.yaml
witness:
public_key: "0x..."
mode: "standalone"
stake_amount: TBD # DIG tokens to stake
chia:
full_node_rpc: "https://localhost:8555"
certificate: "/path/to/cert"
wallet_rpc: "https://localhost:9256"
validation:
timeout_seconds: 30
retry_attempts: 2
proof_of_work:
hash_rate: 1000 # hashes per second
max_mining_time: 600 # 10 minutes
difficulty_source: "network" # or "manual"
manual_difficulty: 6 # if source is manual
network:
port: 8447
dig_node_timeout: 30
stake:
minimum_stake: TBD
auto_stake: true
stake_wallet_id: 1
rewards:
auto_claim: true
minimum_claim: 1.0
Integrated with DIG Nodeβ
# dig-node-with-witness.yaml
dig_node:
# ... DIG node config ...
witness:
enabled: true
public_key: "0x..."
mode: "integrated"
share_chia_connection: true
# Cannot validate self due to PoW assignment
proof_of_work:
enabled: true
share_mining_resources: false # Run mining in separate thread
Docker Deploymentβ
FROM python:3.10-slim
# Install witness miner
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY witness-node /app/
WORKDIR /app
# Lightweight - only needs Chia RPC access
EXPOSE 8447
CMD ["python", "witness.py", "--config", "/config/witness.yaml"]
Resource Usageβ
Typical Resource Consumption:
- CPU: <5% average, 20% during validation
- RAM: 200-500MB
- Network: <1MB/hour
- Storage: <1GB (validation history)
- DIG Stake: TBD (locked for 7 days)
Integration Examplesβ
Running with DIG Nodeβ
# dig_node_with_witness.py
class IntegratedDeployment:
def __init__(self):
self.dig_node = DIGNode(config.dig_node)
self.witness_node = WitnessNode(config.witness)
# Share Chia connection
self.witness_node.chia_client = self.dig_node.chia_client
def start(self):
# Start both processes
self.dig_node.start()
self.witness_node.start()
# Witness validates other nodes, not self
self.witness_node.set_exclusion(self.dig_node.node_id)
Monitoring Integrationβ
# Combined metrics
INTEGRATED_METRICS = {
"dig_node_earnings": Gauge("DIG earned from storage"),
"witness_earnings": Gauge("DIG earned from witnessing"),
"total_earnings": Gauge("Combined DIG earnings"),
"validation_success_rate": Gauge("Witness success rate"),
"storage_utilization": Gauge("Plot capacity used")
}
Stake Managementβ
class WitnessStakeManager:
def __init__(self, wallet_client):
self.wallet = wallet_client
self.staked_amount = 0
self.locked_until = 0
async def stake_tokens(self, amount):
if amount < WITNESS_STAKE_REQUIREMENTS["minimum_stake"]:
raise ValueError("Insufficient stake amount")
# Create staking transaction
tx = await self.wallet.create_stake_transaction(
amount=amount,
lockup_period=WITNESS_STAKE_REQUIREMENTS["lockup_period"],
node_type="witness",
node_id=self.witness_id
)
# Broadcast and wait for confirmation
tx_hash = await self.wallet.broadcast_transaction(tx)
await self.wait_for_confirmation(tx_hash)
self.staked_amount = amount
self.locked_until = time.time() + WITNESS_STAKE_REQUIREMENTS["lockup_period"]
async def verify_stake(self, witness_id):
# Check on-chain stake amount
stake_info = await self.query_stake_info(witness_id)
return stake_info.amount >= WITNESS_STAKE_REQUIREMENTS["minimum_stake"]
async def generate_stake_proof(self):
# Generate proof of stake for WitnessCoin
return {
"witness_id": self.witness_id,
"stake_amount": self.staked_amount,
"stake_tx_id": self.stake_tx_id,
"locked_until": self.locked_until,
"signature": self.sign_stake_proof()
}
Sybil Resistanceβ
class SybilPrevention:
def __init__(self):
self.witness_registry = {}
def register_witness(self, public_key, stake_amount):
# Stake requirement prevents cheap sybil attacks
if stake_amount < WITNESS_STAKE_REQUIREMENTS["minimum_stake"]:
raise ValueError("Insufficient stake for registration")
self.witness_registry[public_key] = {
"registered_at": time.time(),
"stake_amount": stake_amount,
"validations": 0,
"successful_validations": 0,
"reputation_score": 0.5 # Start neutral
}
def update_reputation(self, witness_id, validation_result):
witness = self.witness_registry[witness_id]
witness["validations"] += 1
if validation_result.success:
witness["successful_validations"] += 1
# Calculate reputation (success rate with age factor)
age_factor = min(1.0, (time.time() - witness["registered_at"]) / 604800)
success_rate = witness["successful_validations"] / witness["validations"]
witness["reputation_score"] = success_rate * age_factor
Related Documentationβ
- DIG Node - Storage node that witnesses validate
- Validator Node - Epoch reward distribution
- Validation Process - Overall validation flow
- Cryptographic Proofs - Proof specifications