defarm-sdk
Version:
DeFarm SDK - On-premise blockchain data processing and tokenization engine for agriculture supply chain
266 lines (229 loc) • 7.7 kB
JavaScript
/**
* Verification Engine - Wrapper for WASM-protected business logic
* This engine integrates with the WASM processor for duplicate detection and validation
*/
class VerificationEngine {
constructor(config = {}) {
this.config = {
duplicateThreshold: config.duplicateThreshold || 0.85,
strictMode: config.strictMode !== false,
organizationId: config.organizationId,
...config
};
// Load WASM processor if available
this.wasmProcessor = null;
this.loadWasmProcessor();
}
async loadWasmProcessor() {
try {
this.wasmProcessor = require('../../pkg/defarm_processor.js');
console.log('✅ WASM verification engine loaded');
} catch (error) {
console.warn('⚠️ WASM processor not available, using fallback');
}
}
/**
* Check for duplicates using WASM-protected algorithms
*/
async checkDuplicates(newAsset, existingAssets = []) {
if (!this.wasmProcessor || !this.wasmProcessor.detect_duplicates) {
return this.fallbackDuplicateCheck(newAsset, existingAssets);
}
try {
// Prepare data for WASM
const newAssetJson = JSON.stringify(this.prepareAssetForWasm(newAsset));
const existingAssetsJson = JSON.stringify(
existingAssets.map(a => this.prepareAssetForWasm(a))
);
// Call WASM function - algorithms are protected
const resultJson = this.wasmProcessor.detect_duplicates(
newAssetJson,
existingAssetsJson
);
const result = JSON.parse(resultJson);
if (result.error) {
throw new Error(result.error);
}
return {
isDuplicate: result.has_exact_match,
hasPotentialDuplicate: result.has_potential_match,
exactMatches: result.exact_matches || [],
potentialMatches: result.potential_matches || [],
confidence: result.exact_matches?.[0]?.confidence || 0
};
} catch (error) {
console.error('WASM duplicate check failed:', error);
return this.fallbackDuplicateCheck(newAsset, existingAssets);
}
}
/**
* Calculate similarity between two assets
*/
async calculateSimilarity(asset1, asset2) {
if (!this.wasmProcessor || !this.wasmProcessor.calculate_similarity) {
return 0;
}
try {
const asset1Json = JSON.stringify(this.prepareAssetForWasm(asset1));
const asset2Json = JSON.stringify(this.prepareAssetForWasm(asset2));
const similarity = this.wasmProcessor.calculate_similarity(asset1Json, asset2Json);
return similarity;
} catch (error) {
console.error('WASM similarity calculation failed:', error);
return 0;
}
}
/**
* Validate SISBOV (Brazilian livestock tracking)
*/
validateSisbov(sisbov) {
if (!this.wasmProcessor || !this.wasmProcessor.validate_sisbov) {
// Basic fallback validation
return /^\d{15}$/.test(sisbov);
}
try {
return this.wasmProcessor.validate_sisbov(sisbov);
} catch (error) {
console.error('SISBOV validation failed:', error);
return false;
}
}
/**
* Validate asset data
*/
async validateAsset(assetData) {
const errors = [];
const warnings = [];
// Basic validation
if (!assetData.asset_type) {
errors.push('Asset type is required');
}
// Use WASM for advanced validation if available
if (this.wasmProcessor && this.wasmProcessor.validate_agriculture_data) {
try {
const validationResult = this.wasmProcessor.validate_agriculture_data(
JSON.stringify(assetData)
);
const result = JSON.parse(validationResult);
if (!result.valid) {
errors.push(...(result.errors || []));
}
warnings.push(...(result.warnings || []));
} catch (error) {
warnings.push('Advanced validation unavailable');
}
}
// Livestock-specific validation
if (assetData.asset_type === 'livestock') {
if (!assetData.breed) {
warnings.push('Breed information recommended for livestock');
}
if (assetData.identification && assetData.identification.sisbov) {
if (!this.validateSisbov(assetData.identification.sisbov)) {
errors.push('Invalid SISBOV number');
}
}
if (assetData.birth_date) {
const birthDate = new Date(assetData.birth_date);
const now = new Date();
if (birthDate > now) {
errors.push('Birth date cannot be in the future');
}
}
}
// Location validation
if (assetData.location) {
if (!assetData.location.latitude || !assetData.location.longitude) {
warnings.push('GPS coordinates recommended for traceability');
}
}
return {
valid: errors.length === 0,
errors,
warnings,
score: this.calculateQualityScore(assetData, errors, warnings)
};
}
/**
* Prepare asset data for WASM processing
*/
prepareAssetForWasm(asset) {
return {
asset_id: asset.asset_id || asset.id,
identification: asset.identification?.value || asset.identification,
rfid: asset.rfid || asset.identification?.rfid,
ear_tag: asset.ear_tag || asset.identification?.ear_tag,
breed: asset.breed,
location: asset.location ? {
property_id: asset.location.property_id || asset.location.farm_id || 'unknown',
municipality: asset.location.municipality || asset.location.city || 'unknown',
state: asset.location.state || 'unknown',
country: asset.location.country || 'Brazil'
} : null,
timestamp: asset.timestamp || asset.created_at || Date.now(),
organization_id: asset.organization_id || this.config.organizationId
};
}
/**
* Fallback duplicate check when WASM is not available
*/
fallbackDuplicateCheck(newAsset, existingAssets) {
// Simple fallback - check exact ID matches only
const exactMatches = existingAssets.filter(existing => {
if (newAsset.asset_id && existing.asset_id === newAsset.asset_id) {
return true;
}
if (newAsset.rfid && existing.rfid === newAsset.rfid) {
return true;
}
if (newAsset.identification && existing.identification === newAsset.identification) {
return true;
}
return false;
});
return {
isDuplicate: exactMatches.length > 0,
hasPotentialDuplicate: false,
exactMatches: exactMatches.map(a => ({
asset_id: a.asset_id,
confidence: 1.0
})),
potentialMatches: [],
confidence: exactMatches.length > 0 ? 1.0 : 0
};
}
/**
* Calculate quality score for asset data
*/
calculateQualityScore(assetData, errors, warnings) {
let score = 100;
// Deduct for errors
score -= errors.length * 20;
// Deduct for warnings
score -= warnings.length * 5;
// Bonus for complete data
const requiredFields = ['asset_type', 'location', 'timestamp'];
const providedFields = requiredFields.filter(f => assetData[f]);
score += (providedFields.length / requiredFields.length) * 10;
// Bonus for certifications
if (assetData.certifications && assetData.certifications.length > 0) {
score += 10;
}
// Bonus for sustainability metrics
if (assetData.metrics && assetData.metrics.sustainability_index) {
score += 5;
}
return Math.max(0, Math.min(100, score));
}
/**
* Get verification statistics
*/
getStats() {
return {
wasmAvailable: !!this.wasmProcessor,
strictMode: this.config.strictMode,
duplicateThreshold: this.config.duplicateThreshold
};
}
}
module.exports = { VerificationEngine };