This commit implements a complete blockchain-based plant tracking system that preserves lineage across clones, seeds, and all plant offspring while connecting growers through geographic proximity. Features implemented: - Custom blockchain with proof-of-work consensus - Plant registration and cloning with lineage tracking - Geographic discovery to find nearby plants and growers - Integration with plants.net API for plant identification - Comprehensive web UI for plant management - RESTful API endpoints for all operations - Network statistics and visualization Core Components: - lib/blockchain/: PlantBlock, PlantChain, and blockchain manager - lib/services/: plants.net API and geolocation services - pages/api/plants/: REST API endpoints for all operations - pages/: Frontend UI pages for registration, exploration, and lineage Technical Details: - TypeScript for type safety - Next.js for server-side rendering - Tailwind CSS for responsive design - JSON file-based blockchain storage - Haversine distance calculations for geolocation - OpenStreetMap integration for geocoding This system enables large-scale adoption by: - Making plant lineage tracking accessible to everyone - Connecting local communities through plant sharing - Providing immutable proof of plant provenance - Supporting unlimited generations of plant propagation - Scaling from individual growers to global networks Documentation includes comprehensive README with: - Quick start guide - API reference - Architecture details - Scaling recommendations - Use cases for various audiences - Roadmap for future enhancements
106 lines
2.3 KiB
TypeScript
106 lines
2.3 KiB
TypeScript
import crypto from 'crypto';
|
|
import { PlantData, BlockData } from './types';
|
|
|
|
/**
|
|
* PlantBlock - Represents a single block in the plant blockchain
|
|
* Each block contains data about a plant and its lineage
|
|
*/
|
|
export class PlantBlock {
|
|
public index: number;
|
|
public timestamp: string;
|
|
public plant: PlantData;
|
|
public previousHash: string;
|
|
public hash: string;
|
|
public nonce: number;
|
|
|
|
constructor(
|
|
index: number,
|
|
timestamp: string,
|
|
plant: PlantData,
|
|
previousHash: string = ''
|
|
) {
|
|
this.index = index;
|
|
this.timestamp = timestamp;
|
|
this.plant = plant;
|
|
this.previousHash = previousHash;
|
|
this.nonce = 0;
|
|
this.hash = this.calculateHash();
|
|
}
|
|
|
|
/**
|
|
* Calculate the hash of this block using SHA-256
|
|
*/
|
|
calculateHash(): string {
|
|
return crypto
|
|
.createHash('sha256')
|
|
.update(
|
|
this.index +
|
|
this.previousHash +
|
|
this.timestamp +
|
|
JSON.stringify(this.plant) +
|
|
this.nonce
|
|
)
|
|
.digest('hex');
|
|
}
|
|
|
|
/**
|
|
* Mine the block using proof-of-work algorithm
|
|
* Difficulty determines how many leading zeros the hash must have
|
|
*/
|
|
mineBlock(difficulty: number): void {
|
|
const target = Array(difficulty + 1).join('0');
|
|
|
|
while (this.hash.substring(0, difficulty) !== target) {
|
|
this.nonce++;
|
|
this.hash = this.calculateHash();
|
|
}
|
|
|
|
console.log(`Block mined: ${this.hash}`);
|
|
}
|
|
|
|
/**
|
|
* Convert block to JSON for storage/transmission
|
|
*/
|
|
toJSON(): BlockData {
|
|
return {
|
|
index: this.index,
|
|
timestamp: this.timestamp,
|
|
plant: this.plant,
|
|
previousHash: this.previousHash,
|
|
hash: this.hash,
|
|
nonce: this.nonce,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Create a PlantBlock from JSON data
|
|
*/
|
|
static fromJSON(data: BlockData): PlantBlock {
|
|
const block = new PlantBlock(
|
|
data.index,
|
|
data.timestamp,
|
|
data.plant,
|
|
data.previousHash
|
|
);
|
|
block.hash = data.hash;
|
|
block.nonce = data.nonce;
|
|
return block;
|
|
}
|
|
|
|
/**
|
|
* Verify if this block is valid
|
|
*/
|
|
isValid(previousBlock?: PlantBlock): boolean {
|
|
// Check if hash is correct
|
|
if (this.hash !== this.calculateHash()) {
|
|
return false;
|
|
}
|
|
|
|
// Check if previous hash matches
|
|
if (previousBlock && this.previousHash !== previousBlock.hash) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|