Skip to main content

PlotCoin

💜 Support the DIG Network

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

PlotCoin is an on-chain registry mechanism that maps capsule identifiers to storage providers with cryptographic proof packages. Each PlotCoin serves as a verifiable claim of data custody, enabling decentralized discovery and validation. PlotCoins maintain a constant size (~5KB) regardless of the plot size, making them extremely efficient for blockchain storage.

Binary Format

On-Chain Structure

class PlotCoin:
def __init__(self):
self.version: int = 0x01 # 4 bytes: 0x01 for v1
self.plotcoin_id: bytes = bytes(32) # 32 bytes: SHA-256 identifier
self.capsule_id: bytes = bytes(32) # 32 bytes: Target capsule ID
self.owner_public_key: bytes = bytes(48) # 48 bytes: BLS public key
self.creation_timestamp: int = 0 # 8 bytes: Unix timestamp
self.chia_block_height: int = 0 # 8 bytes: Creation block
self.chia_block_hash: bytes = bytes(32) # 32 bytes: Block hash
self.staking_amount: int = 0 # 8 bytes: DIG tokens staked
self.network_location: bytes = bytes(64) # 64 bytes: Network binding
self.network_location_sig: bytes = bytes(96) # 96 bytes: BLS signature
self.proof_package_size: int = 0 # 4 bytes: Proof size
self.proof_package: bytes = b'' # Variable length
self.plotcoin_signature: bytes = bytes(96) # 96 bytes: Final signature

Cryptographic Proof Package

class CryptographicProofPackage:
def __init__(self):
self.package_version: int = 0 # 4 bytes: Format version
self.proof_count: int = 4 # 4 bytes: Always 4 for v1
self.package_hash: bytes = bytes(32) # 32 bytes: SHA-256 hash
self.generation_timestamp: int = 0 # 8 bytes: Generation time

# Four required proofs
self.plot_ownership_proof: CryptographicProof = None
self.data_inclusion_proof: CryptographicProof = None
self.computational_work_proof: CryptographicProof = None
self.physical_access_proof: CryptographicProof = None

self.proof_aggregation: bytes = bytes(32) # 32 bytes: Aggregated proofs
self.verification_metadata: bytes = bytes(64) # 64 bytes: Verification data

Cryptographic Proof Format

class CryptographicProof:
def __init__(self):
self.proof_type: int = 0 # 4 bytes: Type identifier
self.proof_version: int = 0 # 4 bytes: Proof format version
self.proof_data: bytes = bytes(256) # 256 bytes: Compressed proof data
self.public_input_hash: bytes = bytes(32) # 32 bytes: Public inputs hash
self.commitment_hash: bytes = bytes(32) # 32 bytes: Proof commitments
self.nonce: bytes = bytes(32) # 32 bytes: Unique nonce
self.generation_timestamp: int = 0 # 8 bytes: Generation time
self.auxiliary_data: bytes = bytes(32) # 32 bytes: Proof-specific data
self.generator_signature: bytes = bytes(96) # 96 bytes: BLS signature

Size Analysis

Constant 5KB Breakdown

ComponentSize (bytes)Description
PlotCoin header344Fixed metadata
Network location64Fixed binding
Network signature96BLS signature
Proof package header144Package meta
4 × Cryptographic proofs1,984496 bytes each
Package footer96Aggregations
PlotCoin signature96BLS signature
Padding~1,1764KB alignment
Total~5,000~5KB constant

Scalability Advantage

Traditional Proofs:

  • 1GB plot: ~50MB proof
  • 100GB plot: ~5GB proof
  • 1TB plot: ~50GB proof

PlotCoin Proofs:

  • Any size: ~5KB proof (constant)
  • Reduction: 10,000x - 1,000,000x
  • Verification: O(1) time

Network Location

Format Specification

class NetworkLocation:
def __init__(self):
self.address_type: int = 0 # 1 byte: 0x01=IPv4, 0x02=IPv6, 0x03=DNS
self.address: str = '' # 45 bytes: IP or hostname
self.port: int = 0 # 2 bytes: TCP port (big-endian)
self.capabilities: int = 0 # 8 bytes: Service flags
self.binding_timestamp: int = 0 # 8 bytes: Binding time
self.reserved: bytes = bytes(16) # 16 bytes: Future use

Location Signature

def generate_location_signature(location, plotcoin_id, capsule_id, private_key):
message = concat(
location, # 64 bytes
plotcoin_id, # 32 bytes
capsule_id, # 32 bytes
b"DIG_NETWORK_LOCATION_BINDING" # Domain separator
)
return bls_sign(private_key, message)

Anti-Spoofing

  • Signature Required: Location must be signed by owner
  • IP Verification: Claimed IPs must be reachable
  • DNS Consistency: Hostname must resolve correctly
  • Port Accessibility: Service must respond on port

Lifecycle Management

Creation Process

def create_plotcoin(plot_data, capsule_id, network_location, stake, private_key):
# Generate all proofs
proofs = {
'ownership': generate_plot_ownership_proof(plot_data, private_key),
'inclusion': generate_data_inclusion_proof(plot_data, capsule_id),
'work': generate_computational_work_proof(plot_data, capsule_id),
'access': generate_physical_access_proof(plot_data, current_block())
}

# Package proofs
proof_package = package_cryptographic_proofs(proofs)

# Create PlotCoin
plotcoin = PlotCoin(
version=0x01,
plotcoin_id=generate_unique_id(),
capsule_id=capsule_id,
owner_public_key=derive_public_key(private_key),
creation_timestamp=current_time(),
chia_block_height=latest_block_height(),
chia_block_hash=latest_block_hash(),
staking_amount=stake,
network_location=network_location,
network_location_sig=generate_location_signature(...),
proof_package=proof_package
)

# Sign complete PlotCoin
plotcoin.signature = bls_sign(private_key, serialize(plotcoin))
return plotcoin

Epoch-Based Expiration

  • Duration: ~7 days per epoch
  • Purpose: Ensure liveness and fresh proofs
  • Renewal: Requires new proof generation
  • Cleanup: Expired coins become invalid

Renewal Process

def renew_plotcoin(original, plot_data, private_key):
if original.expiration > current_time() + RENEWAL_BUFFER:
raise Error("Not yet eligible for renewal")

# Fresh physical access proof required
new_access_proof = generate_physical_access_proof(
plot_data,
latest_block()
)

# Create new PlotCoin with updated proofs
return create_plotcoin(...)

Proof Registry Architecture

Query Flow:

Validator → Query(capsuleId) → PlotCoin[] → Verify(proofs) → Valid Providers

[PC1, PC2, PC3...]
Each contains:
- Owner identity
- Proof package
- Network location
- Stake amount

Uniqueness Constraint

The tuple (plotId, ownerKey, networkLocation) must be globally unique to prevent duplicate registrations.

Security Model

Cryptographic Guarantees

Each PlotCoin provides mathematical certainty about:

  1. Physical Possession: Owner has actual data copy
  2. Machine Control: Data on controlled hardware
  3. Network Authority: Valid network endpoint ownership
  4. Computational Work: Legitimate proof-of-work binding
  5. Temporal Validity: Current epoch participation

Validation Process

def validate_plotcoin(plotcoin, capsule_id, context):
# Structure validation
if not verify_structure(plotcoin):
return Invalid("Invalid structure")

# Signature verification
if not verify_signatures(plotcoin):
return Invalid("Signature failure")

# Expiration check
if plotcoin.creation_timestamp + VALIDITY_PERIOD < current_time():
return Invalid("Expired")

# Staking verification
if get_staked_amount(plotcoin.id) < MIN_STAKE:
return Invalid("Insufficient stake")

# Cryptographic proof verification
proof_result = verify_cryptographic_proofs(plotcoin.proof_package, capsule_id, context)
if not proof_result.valid:
return Invalid("Proof failure")

# Network liveness
liveness = test_network_access(plotcoin.network_location, capsule_id)
if not liveness.accessible:
return Invalid("Network unreachable")

return Valid(
confidence=proof_result.confidence,
response_time=liveness.response_time
)

Fraud Detection

Detectable Fraud:

  • Forged proof data
  • Duplicate registration
  • Network spoofing
  • Stale proofs
  • Signature forgery

Penalties:

  • Immediate disqualification
  • Stake slashing
  • Epoch bans
  • Reputation damage

Economic Mechanics

Staking Requirements

  • Minimum Stake: Prevents spam registrations
  • Linear Scaling: More capsules require more stake
  • Refundable: Stake returned on coin expiration
  • Slashing: Penalties for protocol violations

Cost Structure

Total Cost = Base Fee + (Stake × Capsule Count) + Transaction Fee

Where:
- Base Fee: Network participation cost
- Stake: Per-capsule collateral requirement
- Transaction Fee: Blockchain execution cost

Blockchain Integration

Chia DataLayer Storage

# PlotCoin operations
def store_plotcoin(plotcoin):
# Atomic operations
stake_tokens(plotcoin.staking_amount)
store_in_datalayer(plotcoin)
register_for_discovery(plotcoin.capsule_id, plotcoin.id)
emit_event("PlotCoinCreated", plotcoin.id)

def expire_plotcoin(plotcoin_id):
mark_expired(plotcoin_id)
release_stake(plotcoin_id)
remove_from_discovery(plotcoin_id)
emit_event("PlotCoinExpired", plotcoin_id)

Smart Contract Integration

class PlotCoinRegistry:
"""Smart contract interface for PlotCoin management"""

def create_plotcoin(self, data: bytes, stake: int, sender: str):
"""Create a new PlotCoin with staking"""
if stake < MIN_STAKE:
raise ValueError("Insufficient stake")

# Escrow tokens
self.escrow_tokens(sender, stake)

# Store PlotCoin
plotcoin_id = self.store_plotcoin(data)

# Emit event
self.emit_event("PlotCoinCreated", plotcoin_id)
return plotcoin_id

def slash_fraud(self, plotcoin_id: bytes, proof: bytes):
"""Slash a fraudulent PlotCoin"""
if not self.verify_fraud(proof):
raise ValueError("Invalid fraud proof")

# Transfer stake to validator pool
self.transfer_stake(plotcoin_id, self.validator_pool)

# Ban the owner
owner = self.get_owner(plotcoin_id)
self.ban_owner(owner)

# Emit event
self.emit_event("FraudDetected", plotcoin_id)

Chia Singleton Pattern

PlotCoins utilize Chia's coin model:

  • Singleton pattern for uniqueness
  • Chialisp puzzles for validation logic
  • Offer system for stake management
  • State channels for bulk operations

Integration Points

Validator Interface

class PlotCoinRegistry:
def query_by_capsule(self, capsule_id: bytes32) -> List[PlotCoin]:
"""Returns all PlotCoins claiming to store capsule"""

def verify_proofs(self, coin: PlotCoin) -> ProofValidity:
"""Validates embedded proof package"""

def check_liveness(self, location: NetworkLocation) -> bool:
"""Tests actual data availability"""

Reward Distribution

PlotCoins interface with the reward system:

  1. Selection: Random capsule chosen for validation
  2. Discovery: Query PlotCoins for selected capsule
  3. Verification: Validate proofs and liveness
  4. Distribution: Qualifying owners receive rewards

Plot Format Reference

PlotCoins prove ownership and storage of plots, which follow a specific binary format. Understanding the plot structure is essential for generating and verifying the cryptographic proofs contained in PlotCoins.

Plot Overview

The DIG Plot format implements a streaming-optimized storage architecture with proof-of-work validation and cryptographic chaining. Each plot contains capsules with computational proof requirements, creating an immutable chain of work bound to specific data.

Plot File Structure

┌─────────────────────────────────────────────────────────┐
│ Section │ Offset │ Size │ Align │
├────────────────────┼─────────────┼───────────┼─────────┤
│ File Header │ 0x0000 │ 64B │ - │
│ Metadata Section │ Variable │ Variable │ 4096B │
│ Index Section │ Variable │ Variable │ 4096B │
│ Table Section │ Variable │ Variable │ 4096B │
│ Data Section │ Variable │ Variable │ 4096B │
│ Verification │ Variable │ Variable │ 4096B │
└─────────────────────────────────────────────────────────┘

Plot File Header (64 bytes)

interface PlotFileHeader {
magicNumber: Buffer; // 4 bytes: 0x44494750 ("DIGP")
version: number; // 4 bytes: File format version
fileSize: bigint; // 8 bytes: Total file size
createdAt: number; // 8 bytes: Unix timestamp
sealedAt: number; // 8 bytes: Unix timestamp when sealed
tableCount: number; // 4 bytes: Number of tables
capsuleCount: number; // 4 bytes: Number of capsules
totalDataSize: bigint; // 8 bytes: Total data size
headerChecksum: Buffer; // 8 bytes: Header integrity checksum
reserved: Buffer; // 8 bytes: Reserved for future use
}

Plot Metadata Section

interface PlotMetadataSection {
plotId: Buffer; // 32 bytes: SHA-256 hash for unique identification
publicKey: Buffer; // 32 bytes: Chia synthetic key
keyCommitment: Buffer; // 32 bytes: Key commitment binding
merkleRoot: Buffer; // 32 bytes: Merkle root of all data
difficulty: number; // 4 bytes: Proof-of-work difficulty level
chiaBlockHeight: number; // 4 bytes: Chia block height for temporal anchoring
chiaBlockHash: Buffer; // 32 bytes: Chia block hash for temporal anchoring
compressionAlgorithm: number; // 4 bytes: Compression algorithm type
compressionLevel: number; // 4 bytes: Compression level
flags: number; // 4 bytes: Feature flags
customData: Buffer; // Variable: Custom metadata
}

Table Architecture

Plots use a flexible table structure with data and proof tables:

  • Data Tables (odd indices: 1, 3, 5): Store actual capsule content
  • Proof Tables (even indices: 0, 2, 4, 6): Store cryptographic proofs and security padding
interface PlotTable {
index: number; // Table number
type: 'PROOF' | 'DATA'; // Table type
hash: Buffer; // Table hash after proof-of-work
data: Buffer; // Table content
prevTableHash: Buffer; // Previous table hash (chaining)
nonce: number; // Proof-of-work nonce
workDifficulty: number; // Achieved difficulty
createdAt: number; // Creation timestamp
dataSize: number; // Size of table data
}

Proof-of-Work Implementation

Each table requires proof-of-work with dual binding:

import hashlib

# Dual binding: work is bound to both plot and capsule content
def compute_proof_of_work(table_data_commitment, previous_hash, nonce, public_key, combined_capsule_hash):
hash_input = b''.join([
table_data_commitment, # Commitment to table data (32 bytes)
previous_hash, # Previous table hash (32 bytes)
str(nonce).encode(), # Current nonce attempt
public_key, # Plot public key (48 bytes)
combined_capsule_hash # Combined capsule hash for dual binding
])

candidate = hashlib.sha256(hash_input).digest()

# Verify difficulty: count leading zero bits
is_valid = CryptoUtils.verify_work_difficulty(candidate, difficulty)
return candidate, is_valid

Capsule Storage Format

Each capsule is serialized with metadata:

# Capsule serialization format
def serialize_capsule(capsule_id, capsule_data, metadata):
import struct
import json

id_buffer = capsule_id.encode('utf-8')
metadata_buffer = json.dumps(metadata).encode('utf-8')
capsule_hash = hashlib.sha256(capsule_data).digest()

serialized = b''.join([
struct.pack('<H', len(id_buffer)), # 2 bytes: ID length
id_buffer, # Variable: Capsule ID
struct.pack('<I', len(capsule_data)), # 4 bytes: Data size
struct.pack('<H', len(metadata_buffer)), # 2 bytes: Metadata length
metadata_buffer, # Variable: JSON metadata
capsule_hash, # 32 bytes: SHA-256 of content
struct.pack('<Q', int(time.time())), # 8 bytes: Creation timestamp
capsule_data # Variable: Actual capsule data
])

return serialized

Streaming Architecture

The plot format is designed for streaming operations:

  • Chunk size: 64KB default (configurable)
  • Memory usage: Constant regardless of plot size
  • Processing: Never loads entire plots into memory
  • Alignment: 4096-byte boundaries for optimal I/O
  • Buffering: High-water mark streaming with backpressure

Plot Proof Package Structure

The proof package referenced in PlotCoins contains:

interface PlotProofPackage {
capsuleId: string;
plotId: Buffer; // Links all proofs together
capsuleHash: Buffer; // The specific capsule being proven
plotSeedCommitment: Buffer; // Plot-specific seed for work binding

// 1. Plot Ownership Proof (Digital Signature)
plotOwnershipProof: {
publicKey: Buffer; // Owner's public key
merkleRoot: Buffer; // Plot's merkle root
difficulty: number; // Claimed difficulty
blockHeight: number; // Chia anchor block
blockHash: Buffer; // Chia anchor hash
signature: Buffer; // DataLayer signature
signedMessage: Buffer; // Message that was signed
};

// 2. Data Inclusion Proof (Merkle Proof)
dataInclusionProof: {
merklePath: Buffer[]; // Sibling hashes
pathDirections: Buffer; // Packed directions
leafIndex: number; // Position in tree
};

// 3. Computational Work Proof (Proof of Work)
computationalWorkProof: {
nonce: Buffer; // Found nonce
tableDataHash: Buffer; // Table data hash
previousHash: Buffer; // Previous table hash
workDifficulty: number; // Achieved difficulty
};

// 4. Physical Access Proof (Fixed Window + Merkle)
physicalAccessProof: {
blockHeight: number; // Current Chia block
blockHash: Buffer; // Current Chia hash
chunkIndices: number[]; // Selected chunks
chunkData: Buffer[]; // Actual chunk data
chunkProofs: MerklePathProof[]; // Merkle proofs
capsuleRootHash: Buffer; // Capsule's merkle root
totalChunks: number; // Total chunks in capsule
};
}

Plot Security Properties

  • Non-repudiation: BLS signatures compatible with Chia
  • Integrity: Hash chains and Merkle trees
  • Authenticity: Chia key binding and temporal anchoring
  • Freshness: Blockchain anchoring with block height/hash
  • Work binding: Dual binding to both plot and capsule content

Plot Implementation Limits

PropertyLimitRationale
Max plot size256TBPractical storage limits
Max capsules per plot4.2 billion32-bit addressing
Max capsule size4GBSingle capsule limit
Header size64 bytesMinimal overhead
Chunk size64KB defaultStreaming optimization
Table alignment4096 bytesI/O optimization

Performance Characteristics

  • Query Complexity: O(1) by capsule ID
  • Verification Time: ~100ms per proof package
  • Creation Cost: ~0.001 XCH in fees (market dependent)
  • Storage Overhead: ~5KB per PlotCoin
  • Processing: ~1M PlotCoins/minute verification rate

Optimization Strategies

  • Batch minting: Multiple PlotCoins per transaction
  • Proof caching: Reuse proofs within epoch
  • Parallel verification: Concurrent proof checking
  • Merkle aggregation: Combine multiple capsules