# Transport Tracking Architecture Deep dive into the seed-to-seed transport tracking system. ## Architecture Overview ``` ┌─────────────────────────────────────────────────────────────────────┐ │ TRANSPORT TRACKING SYSTEM │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ TRANSPORT CHAIN │ │ │ │ │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ │ │Genesis │→ │ Block 1 │→ │ Block 2 │→ │ Block N │ │ │ │ │ │ │ │ Seed │ │ Plant │ │ Harvest │ │ │ │ │ │ │ │ Acquire │ │ │ │ │ │ │ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ │ ┌──────────────────┼──────────────────┐ │ │ ▼ ▼ ▼ │ │ ┌────────────────┐ ┌──────────────┐ ┌───────────────┐ │ │ │ Event Index │ │ Plant Index │ │ Batch Index │ │ │ │ (by event ID) │ │ (by plant ID)│ │ (by batch ID) │ │ │ └────────────────┘ └──────────────┘ └───────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ SERVICES │ │ │ │ │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ │ │Distance │ │ Carbon │ │ QR │ │ Journey │ │ │ │ │ │Calculator│ │Calculator│ │Generator│ │ Builder │ │ │ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘ ``` ## TransportChain Class ### Core Structure ```typescript class TransportChain { // Blockchain data public chain: TransportBlock[]; public difficulty: number; // Indexes for fast lookups private eventIndex: Map; private plantEvents: Map; private batchEvents: Map; // Core operations recordEvent(event: TransportEvent): TransportBlock; getPlantJourney(plantId: string): PlantJourney | null; getEnvironmentalImpact(userId: string): EnvironmentalImpact; generateQRData(plantId?: string, batchId?: string): TransportQRData; isChainValid(): boolean; } ``` ### Block Creation Flow ``` Event Input │ ▼ ┌─────────────────────┐ │ Calculate Distance │ (if not provided) │ Haversine Formula │ └──────────┬──────────┘ │ ▼ ┌─────────────────────┐ │ Calculate Carbon │ (if not provided) │ factor × distance │ × weight └──────────┬──────────┘ │ ▼ ┌─────────────────────┐ │ Create Block │ │ - index │ │ - timestamp │ │ - event data │ │ - previousHash │ │ - cumulative stats │ └──────────┬──────────┘ │ ▼ ┌─────────────────────┐ │ Mine Block │ │ Find nonce where │ │ hash starts with │ │ N zeros │ └──────────┬──────────┘ │ ▼ ┌─────────────────────┐ │ Add to Chain │ │ Update Indexes │ └──────────┬──────────┘ │ ▼ Return Block ``` ## Event Processing ### Event Type Handlers Each event type extracts different IDs for indexing: ```typescript function extractPlantIds(event: TransportEvent): string[] { switch (event.eventType) { case 'planting': return event.plantIds; case 'growing_transport': return event.plantIds; case 'harvest': return event.plantIds; case 'seed_saving': return event.parentPlantIds; default: return []; } } function extractBatchIds(event: TransportEvent): string[] { switch (event.eventType) { case 'seed_acquisition': return [event.seedBatchId]; case 'planting': return [event.seedBatchId]; case 'harvest': const ids = [event.harvestBatchId]; if (event.seedBatchIdCreated) ids.push(event.seedBatchIdCreated); return ids; case 'processing': return [...event.harvestBatchIds, event.processingBatchId]; case 'distribution': case 'consumer_delivery': return event.batchIds; case 'seed_saving': return [event.newSeedBatchId]; case 'seed_sharing': return [event.seedBatchId]; default: return []; } } ``` ## Distance Calculation ### Haversine Formula ```typescript static calculateDistance( from: TransportLocation, to: TransportLocation ): number { const R = 6371; // Earth's radius in km const dLat = toRadians(to.latitude - from.latitude); const dLon = toRadians(to.longitude - from.longitude); const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(toRadians(from.latitude)) * Math.cos(toRadians(to.latitude)) * Math.sin(dLon / 2) * Math.sin(dLon / 2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return R * c; // Distance in kilometers } ``` ### Accuracy Notes | Distance | Accuracy | |----------|----------| | < 10 km | ± 10 meters | | 10-100 km | ± 100 meters | | 100-1000 km | ± 1 km | | > 1000 km | ± 10 km | ## Carbon Calculation ### Weight Estimation ```typescript function estimateWeight(event: TransportEvent): number { switch (event.eventType) { case 'seed_acquisition': case 'seed_saving': case 'seed_sharing': return 0.1; // Seeds are light case 'planting': return 0.5; // Seedlings case 'growing_transport': return 2; // Potted plants case 'harvest': return event.netWeight || 5; case 'processing': return event.outputWeight || 5; case 'distribution': case 'consumer_delivery': return 5; // Default produce weight default: return 1; } } ``` ### Carbon Formula ```typescript function calculateCarbon( method: TransportMethod, distanceKm: number, weightKg: number ): number { const factor = CARBON_FACTORS[method] || 0.1; return factor * distanceKm * weightKg; } ``` ## Journey Reconstruction ### Algorithm ```typescript function getPlantJourney(plantId: string): PlantJourney | null { // 1. Get all events for this plant const events = plantEvents.get(plantId); if (!events || events.length === 0) return null; // 2. Sort by timestamp const sorted = [...events].sort( (a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime() ); // 3. Calculate totals let totalFoodMiles = 0; let totalCarbonKg = 0; let daysInTransit = 0; for (const event of sorted) { totalFoodMiles += event.distanceKm; totalCarbonKg += event.carbonFootprintKg; daysInTransit += event.durationMinutes / (60 * 24); } // 4. Determine current stage const currentStage = determineStage(sorted); // 5. Find lineage info const seedAcquisition = sorted.find(e => e.eventType === 'seed_acquisition'); const planting = sorted.find(e => e.eventType === 'planting'); // 6. Build journey object return { plantId, seedBatchOrigin: seedAcquisition?.seedBatchId || planting?.seedBatchId, currentCustodian: sorted[sorted.length - 1].receiverId, currentLocation: sorted[sorted.length - 1].toLocation, currentStage, events: sorted, totalFoodMiles, totalCarbonKg, daysInTransit: Math.round(daysInTransit), daysGrowing: calculateGrowingDays(sorted), generation: seedAcquisition?.generation || 0, ancestorPlantIds: seedAcquisition?.parentPlantIds || [], descendantSeedBatches: findDescendantSeeds(sorted) }; } ``` ## QR Code Generation ### Data Structure ```typescript interface TransportQRData { plantId?: string; batchId?: string; blockchainAddress: string; // First 42 chars of latest hash quickLookupUrl: string; // Web URL for tracking lineageHash: string; // SHA256 of all events (16 chars) currentCustodian: string; // Current holder lastEventType: TransportEventType; lastEventTimestamp: string; verificationCode: string; // Random 8-char code } ``` ### Generation Process ``` ┌─────────────────────┐ │ Get events for │ │ plant/batch │ └──────────┬──────────┘ │ ▼ ┌─────────────────────┐ │ Calculate lineage │ │ hash (SHA256) │ └──────────┬──────────┘ │ ▼ ┌─────────────────────┐ │ Get blockchain │ │ address (latest │ │ block hash) │ └──────────┬──────────┘ │ ▼ ┌─────────────────────┐ │ Generate │ │ verification code │ └──────────┬──────────┘ │ ▼ ┌─────────────────────┐ │ Build QR data │ │ object │ └──────────┬──────────┘ │ ▼ Return QRData ``` ## Chain Validation ### Validation Checks ```typescript function isChainValid(): boolean { for (let i = 1; i < chain.length; i++) { const current = chain[i]; const previous = chain[i - 1]; // Check 1: Hash integrity const expectedHash = calculateHash( current.index, current.timestamp, current.transportEvent, current.previousHash, current.nonce ); if (current.hash !== expectedHash) { return false; // Hash has been tampered } // Check 2: Chain linkage if (current.previousHash !== previous.hash) { return false; // Chain is broken } // Check 3: Proof of work const target = '0'.repeat(difficulty); if (current.hash.substring(0, difficulty) !== target) { return false; // PoW not satisfied } } return true; } ``` ## Storage and Persistence ### Export Format ```json { "difficulty": 3, "chain": [ { "index": 0, "timestamp": "2024-01-01T00:00:00Z", "transportEvent": { ... }, "previousHash": "0", "hash": "000abc...", "nonce": 12345, "cumulativeCarbonKg": 0, "cumulativeFoodMiles": 0, "chainLength": 1 } ] } ``` ### Import Process ```typescript static fromJSON(data: any): TransportChain { const chain = new TransportChain(data.difficulty); chain.chain = data.chain; // Rebuild indexes from chain for (const block of chain.chain) { chain.indexEvent(block.transportEvent, block); } return chain; } ``` ## Performance Optimization ### Indexing Strategy | Index | Use Case | Complexity | |-------|----------|------------| | eventIndex | Event lookup by ID | O(1) | | plantEvents | Plant journey | O(1) | | batchEvents | Batch tracking | O(1) | ### Mining Optimization ```typescript // Parallel mining hint (future) // Can be parallelized across CPU cores function mineBlockParallel(block: TransportBlock): void { const workers = os.cpus().length; const ranges = divideNonceRange(0, MAX_NONCE, workers); // Each worker tries nonces in its range // First to find valid hash wins } ``` ## Integration Points ### With Plant Blockchain ``` Transport Chain Plant Chain ────────────── ─────────── plantIds in events ←─────────→ plantId in registrations harvestBatchId ←─────────→ linked to plant seedBatchId ←─────────→ lineage reference ``` ### With Vertical Farm Controller ``` VF Controller Transport Chain ───────────── ─────────────── startCropBatch() ─creates─→ planting event completeHarvest() ─creates─→ harvest event ─creates─→ distribution event ``` ### With Demand Forecaster ``` Demand Forecaster Transport Chain ───────────────── ─────────────── supply matching ─tracks─→ distribution events market matches ─creates─→ consumer_delivery events ```