PlotCoin
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
Component | Size (bytes) | Description |
---|---|---|
PlotCoin header | 344 | Fixed metadata |
Network location | 64 | Fixed binding |
Network signature | 96 | BLS signature |
Proof package header | 144 | Package meta |
4 × Cryptographic proofs | 1,984 | 496 bytes each |
Package footer | 96 | Aggregations |
PlotCoin signature | 96 | BLS signature |
Padding | ~1,176 | 4KB 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:
- Physical Possession: Owner has actual data copy
- Machine Control: Data on controlled hardware
- Network Authority: Valid network endpoint ownership
- Computational Work: Legitimate proof-of-work binding
- 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:
- Selection: Random capsule chosen for validation
- Discovery: Query PlotCoins for selected capsule
- Verification: Validate proofs and liveness
- 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
Property | Limit | Rationale |
---|---|---|
Max plot size | 256TB | Practical storage limits |
Max capsules per plot | 4.2 billion | 32-bit addressing |
Max capsule size | 4GB | Single capsule limit |
Header size | 64 bytes | Minimal overhead |
Chunk size | 64KB default | Streaming optimization |
Table alignment | 4096 bytes | I/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
Related Documentation
- Plots - Storage container specification
- Proof of Living Storage - Cryptographic proof details
- Rewards Distributor - Reward mechanism
- Token Model - DIG token economics