Add cryptographic hashing for waste verification using IPFS content identifiers to enable immutable verification of waste documentation and images.
The recycling system needs a way to:
- Store immutable references to waste images and documents
- Verify waste authenticity through cryptographic hashes
- Support multiple document types per waste
- Prevent hash tampering
- Enable decentralized storage integration
- CIDv0 (Content Identifier v0): "Qm" prefix + 44 characters
- CIDv1 (Content Identifier v1): "bafy" prefix + 52 characters
- Both formats are IPFS-compatible
pub struct Waste {
// ... existing fields ...
pub image_hash: Option<String>, // Primary image IPFS hash
pub document_hashes: Vec<String>, // Supporting documents (max 5)
}pub fn set_waste_image(
env: Env,
waste_id: u128,
hash: String,
caller: Address
) -> WastePurpose: Set or replace the primary image hash for waste
Parameters:
env: Soroban environmentwaste_id: ID of wastehash: IPFS hash (CIDv0 or CIDv1)caller: Address setting the hash (must be owner)
Validation:
- Caller must be registered participant
- Caller must be waste owner
- Waste must be active
- Hash must be valid IPFS format
Side Effects:
- Sets or replaces
image_hash - Stores updated waste
- Emits event on update
- Replaces previous hash if exists
Returns: Updated Waste struct
Errors:
- "Waste not found" - waste_id doesn't exist
- "Waste is deactivated" - waste inactive
- "Only the waste owner can set image hash" - not owner
- "Invalid IPFS hash" - hash format invalid
pub fn add_waste_document(
env: Env,
waste_id: u128,
hash: String,
caller: Address
) -> WastePurpose: Add a supporting document hash to waste
Parameters:
env: Soroban environmentwaste_id: ID of wastehash: IPFS hash (CIDv0 or CIDv1)caller: Address adding document (must be owner)
Validation:
- Caller must be registered participant
- Caller must be waste owner
- Waste must be active
- Hash must be valid IPFS format
- Document count must be < 5
Side Effects:
- Appends hash to
document_hashesvector - Stores updated waste
- Emits event on addition
- Enforces 5-document limit
Returns: Updated Waste struct
Errors:
- "Waste not found" - waste_id doesn't exist
- "Waste is deactivated" - waste inactive
- "Only the waste owner can add document hashes" - not owner
- "Invalid IPFS hash" - hash format invalid
- "Document hash limit reached" - already 5 documents
fn validate_ipfs_hash(env: &Env, hash: &String) -> boolPurpose: Validate IPFS hash format
Parameters:
env: Soroban environmenthash: Hash string to validate
Validation Rules:
- CIDv0: Starts with "Qm" and is exactly 46 characters
- CIDv1: Starts with "bafy" and is exactly 56 characters
- No other formats accepted
Returns: true if valid, panics if invalid
Errors:
- "Invalid IPFS hash" - format doesn't match
Prefix: "Qm"
Length: 46 characters total
Example: QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG
Prefix: "bafy"
Length: 56 characters total
Example: bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi
// Recycler submits waste
let waste_id = client.recycle_waste(
&WasteType::Plastic,
&1000,
&recycler,
&lat,
&lon
);
// Set image hash (CIDv0)
let waste = client.set_waste_image(
&waste_id,
&String::from_str(&env, "QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG"),
&recycler
);
assert!(waste.image_hash.is_some());// Add first document
let waste = client.add_waste_document(
&waste_id,
&String::from_str(&env, "QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG"),
&recycler
);
assert_eq!(waste.document_hashes.len(), 1);
// Add second document (CIDv1)
let waste = client.add_waste_document(
&waste_id,
&String::from_str(&env, "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"),
&recycler
);
assert_eq!(waste.document_hashes.len(), 2);// Set initial image
client.set_waste_image(
&waste_id,
&String::from_str(&env, "QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG"),
&recycler
);
// Replace with new image
let waste = client.set_waste_image(
&waste_id,
&String::from_str(&env, "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"),
&recycler
);
assert_eq!(waste.image_hash.unwrap(), "bafy...");// Add 5 documents (maximum)
for i in 0..5 {
let hash = format!("QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG");
client.add_waste_document(&waste_id, &String::from_str(&env, &hash), &recycler);
}
// 6th document fails
let hash = "QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG";
// This will panic with "Document hash limit reached"
client.add_waste_document(&waste_id, &String::from_str(&env, hash), &recycler);- Hashes provide verification data for confirmation
- Confirmed waste has verified hashes
- Confirmation can validate hash authenticity
- Hashes transferred with waste ownership
- Recipient can verify waste through hashes
- Transfer history includes hash references
- Hashes updated at different processing stages
- Status history can include hash updates
- Final product has manufacturing documentation
- IPFS hashes are content-addressed
- Hash cannot be changed without changing content
- Prevents tampering with waste documentation
- Only waste owner can set/add hashes
- Prevents unauthorized documentation
- Maintains data integrity
- Strict IPFS format validation
- Rejects invalid or malicious hashes
- Prevents injection attacks
- Cannot add hashes to deactivated waste
- Prevents modification of archived waste
- Maintains historical accuracy
- CIDv0 hash acceptance
- CIDv1 hash acceptance
- Invalid hash rejection
- Short hash rejection
- Owner-only access for image
- Owner-only access for documents
- Document hash addition
- Document limit enforcement
- Deactivated waste protection
- Hash replacement
- New waste initialization
- Multiple document types
- Hash + confirmation flow
- Hash + transfer flow
- Hash + processing status
- Multiple hash updates
- Hash verification
- Empty hash string
- Hash with special characters
- Very long hash strings
- Null/None hashes
- Duplicate hashes
- Set image: ~3,000 gas (validation + storage write)
- Add document: ~3,500 gas (validation + storage write)
- Hash validation: ~500 gas (string operations)
- Image hash: ~50 bytes (optional)
- Per document: ~50 bytes
- Max per waste: ~300 bytes (5 documents + image)
1. Upload file to IPFS
2. Get content hash (CID)
3. Call set_waste_image() or add_waste_document()
4. Hash stored on-chain
5. File retrievable from IPFS using hash
# Upload file to IPFS
ipfs add waste_image.jpg
# Returns: QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG
# Store hash on-chain
client.set_waste_image(&waste_id, &hash, &owner)
# Retrieve file from IPFS
ipfs get QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG- Hash Verification: On-chain hash verification
- Multiple Image Types: Different image categories
- Hash Metadata: Store hash creation timestamp
- Hash Expiry: Auto-expire old hashes
- Hash Encryption: Encrypted hash storage
- Hash Pinning: Automatic IPFS pinning
- Hash Analytics: Track hash usage patterns
- Functions implemented
- Hash validation implemented
- Storage schema defined
- Error handling implemented
- Access control validated
- Unit tests written (12 tests)
- Integration tests written
- Documentation complete
- Security review done
- Performance tested
- Related Issue: #557 (Confirmation)
- Related Issue: #558 (Transfer Approval)
- Related Issue: #560 (Processing Status)
- Test File:
tests/waste_hashes_test.rs - IPFS Documentation: https://docs.ipfs.io/
- CID Specification: https://github.com/multiformats/cid