File size: 5,661 Bytes
542f3b7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/**
 * Piclet Verification Helper for Frontend
 * Use this in your Svelte game to generate verified Piclets
 */

// This should match the server's secret key
const PICLET_SECRET_KEY = "piclets-dev-key-change-in-production";

/**
 * Create HMAC-SHA256 signature (requires crypto-js or similar)
 * For browser compatibility, you'll need to include crypto-js:
 * npm install crypto-js
 * or include via CDN: https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js
 */
async function createHMAC(message, key) {
    // Browser-native crypto API version (modern browsers)
    if (window.crypto && window.crypto.subtle) {
        const encoder = new TextEncoder();
        const keyData = encoder.encode(key);
        const messageData = encoder.encode(message);
        
        const cryptoKey = await window.crypto.subtle.importKey(
            'raw',
            keyData,
            { name: 'HMAC', hash: 'SHA-256' },
            false,
            ['sign']
        );
        
        const signature = await window.crypto.subtle.sign('HMAC', cryptoKey, messageData);
        const hashArray = Array.from(new Uint8Array(signature));
        return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
    }
    
    // Fallback: Use crypto-js if available
    if (window.CryptoJS) {
        return CryptoJS.HmacSHA256(message, key).toString();
    }
    
    throw new Error("No crypto implementation available. Include crypto-js or use modern browser.");
}

/**
 * Create verification data for a Piclet
 * Call this when generating a Piclet in your game
 */
async function createPicletVerification(picletData, imageCaption = "", conceptString = "") {
    const timestamp = Math.floor(Date.now() / 1000);
    
    // Core data used for signature
    const coreData = {
        name: picletData.name || "",
        primaryType: picletData.primaryType || "",
        baseStats: picletData.baseStats || {},
        movepool: picletData.movepool || [],
        timestamp: timestamp
    };
    
    // Generation metadata
    const generationData = {
        image_caption: imageCaption,
        concept_string: conceptString,
        generation_method: "official_app"
    };
    
    // Add generation data to core data
    coreData.generation_data = generationData;
    
    // Create deterministic JSON string
    const dataString = JSON.stringify(coreData, null, 0);
    
    // Create signature
    const signature = await createHMAC(dataString, PICLET_SECRET_KEY);
    
    return {
        signature: signature,
        timestamp: timestamp,
        generation_data: generationData,
        verification_version: "1.0"
    };
}

/**
 * Add verification to a Piclet before saving
 * Use this in your PicletGenerator component
 */
async function verifyAndPreparePiclet(picletData, imageCaption = "", conceptString = "") {
    try {
        // Create verification data
        const verification = await createPicletVerification(picletData, imageCaption, conceptString);
        
        // Add verification to Piclet data
        const verifiedPiclet = {
            ...picletData,
            verification: verification
        };
        
        return {
            success: true,
            piclet: verifiedPiclet,
            signature: verification.signature
        };
        
    } catch (error) {
        return {
            success: false,
            error: error.message
        };
    }
}

/**
 * Save a verified Piclet to the server
 * Use this instead of direct API calls
 */
async function saveVerifiedPiclet(gradioClient, picletData, imageFile = null, imageCaption = "", conceptString = "") {
    try {
        // Create verified Piclet
        const verificationResult = await verifyAndPreparePiclet(picletData, imageCaption, conceptString);
        
        if (!verificationResult.success) {
            return {
                success: false,
                error: `Verification failed: ${verificationResult.error}`
            };
        }
        
        // Save to server
        const result = await gradioClient.predict("/save_piclet_api", [
            JSON.stringify(verificationResult.piclet),
            verificationResult.signature,
            imageFile
        ]);
        
        return JSON.parse(result.data[0]);
        
    } catch (error) {
        return {
            success: false,
            error: `Save failed: ${error.message}`
        };
    }
}

/**
 * Example usage in your Svelte component:
 * 
 * // In PicletGenerator.svelte or similar component
 * import { saveVerifiedPiclet } from './verification_helper.js';
 * 
 * async function savePiclet() {
 *     const picletData = {
 *         name: generatedName,
 *         primaryType: detectedType,
 *         baseStats: calculatedStats,
 *         movepool: generatedMoves,
 *         // ... other data
 *     };
 *     
 *     const result = await saveVerifiedPiclet(
 *         gradioClient,
 *         picletData,
 *         uploadedImageFile,
 *         imageCaption,
 *         conceptString
 *     );
 *     
 *     if (result.success) {
 *         console.log(`Piclet saved with ID: ${result.piclet_id}`);
 *     } else {
 *         console.error(`Save failed: ${result.error}`);
 *     }
 * }
 */

// Export for ES modules
if (typeof module !== 'undefined' && module.exports) {
    module.exports = {
        createPicletVerification,
        verifyAndPreparePiclet,
        saveVerifiedPiclet
    };
}

// Global functions for script tag usage
if (typeof window !== 'undefined') {
    window.PicletVerification = {
        createPicletVerification,
        verifyAndPreparePiclet,
        saveVerifiedPiclet
    };
}