diff --git a/packages/wasm-sdk/AI_REFERENCE.md b/packages/wasm-sdk/AI_REFERENCE.md index 23531a9e085..596c2c39b61 100644 --- a/packages/wasm-sdk/AI_REFERENCE.md +++ b/packages/wasm-sdk/AI_REFERENCE.md @@ -763,7 +763,7 @@ Parameters: - `assetLockProofPrivateKey` (string, required) - Asset Lock Proof Private Key - WIF format private key - `publicKeys` (string, required) - Public Keys - - JSON array of public keys + - JSON array of public keys. Key requirements: ECDSA_SECP256K1 requires privateKeyHex or privateKeyWif for signing, BLS12_381 requires privateKeyHex for signing, ECDSA_HASH160 requires either the data field (base64-encoded 20-byte public key hash) or privateKeyHex (produces empty signatures). Example: ```javascript @@ -771,23 +771,25 @@ Example: const assetLockProof = "a9147d3b... (hex-encoded)"; const assetLockProofPrivateKey = "XFfpaSbZq52HPy3WWwe1dXsZMiU1bQn8vQd34HNXkSZThevBWRn1"; // WIF format -// Public keys array with proper key types +// Public keys array with proper key types and private keys for signing/hashing const publicKeys = JSON.stringify([ { id: 0, - type: 0, // ECDSA_SECP256K1 = 0, BLS12_381 = 1, ECDSA_HASH160 = 2 - purpose: 0, // AUTHENTICATION = 0, ENCRYPTION = 1, DECRYPTION = 2, TRANSFER = 3, etc. - securityLevel: 0, // MASTER = 0, CRITICAL = 1, HIGH = 2, MEDIUM = 3 + keyType: "ECDSA_SECP256K1", + purpose: "AUTHENTICATION", + securityLevel: "MASTER", data: "A5GzYHPIolbHkFrp5l+s9IvF2lWMuuuSu3oWZB8vWHNJ", // Base64-encoded public key - readOnly: false + readOnly: false, + privateKeyWif: "XBrZJKcW4ajWVNAU6yP87WQog6CjFnpbqyAKgNTZRqmhYvPgMNV2" }, { id: 1, - type: 0, - purpose: 0, - securityLevel: 2, - data: "AnotherBase64EncodedPublicKeyHere", // Base64-encoded public key - readOnly: false + keyType: "ECDSA_HASH160", + purpose: "AUTHENTICATION", + securityLevel: "HIGH", + data: "ripemd160hash20bytes1234", // Base64-encoded 20-byte RIPEMD160 hash + readOnly: false, + // ECDSA_HASH160 keys produce empty signatures (not required/used for signing) } ]); diff --git a/packages/wasm-sdk/Cargo.toml b/packages/wasm-sdk/Cargo.toml index ced15c058e2..9478867423d 100644 --- a/packages/wasm-sdk/Cargo.toml +++ b/packages/wasm-sdk/Cargo.toml @@ -25,7 +25,7 @@ token_reward_explanations = ["dash-sdk/token_reward_explanations"] [dependencies] dash-sdk = { path = "../rs-sdk", default-features = false } -simple-signer = { path = "../simple-signer" } +simple-signer = { path = "../simple-signer", features = ["state-transitions"] } drive = { path = "../rs-drive", default-features = false, features = ["verify"] } console_error_panic_hook = { version = "0.1.6" } thiserror = { version = "2.0.12" } diff --git a/packages/wasm-sdk/api-definitions.json b/packages/wasm-sdk/api-definitions.json index 0953ed5407a..c2fcfc7bc43 100644 --- a/packages/wasm-sdk/api-definitions.json +++ b/packages/wasm-sdk/api-definitions.json @@ -1234,6 +1234,24 @@ ], "help": "Default adds standard keys for most Platform operations. Advanced allows custom key configuration." }, + { + "name": "keyType", + "type": "select", + "label": "Key Type", + "required": true, + "value": "ECDSA_HASH160", + "options": [ + { + "value": "ECDSA_HASH160", + "label": "ECDSA_HASH160 (Dash Evo Tool default)" + }, + { + "value": "ECDSA_SECP256K1", + "label": "ECDSA_SECP256K1 (Dash mobile wallet default)" + } + ], + "help": "Choose key type" + }, { "name": "keyPreview", "type": "keyPreview", @@ -1261,10 +1279,10 @@ "type": "string", "label": "Public Keys", "required": true, - "description": "JSON array of public keys" + "description": "JSON array of public keys. Key requirements: ECDSA_SECP256K1 requires privateKeyHex or privateKeyWif for signing, BLS12_381 requires privateKeyHex for signing, ECDSA_HASH160 requires either the data field (base64-encoded 20-byte public key hash) or privateKeyHex (produces empty signatures)." } ], - "sdk_example": "// Asset lock proof is a hex-encoded JSON object\nconst assetLockProof = \"a9147d3b... (hex-encoded)\";\nconst assetLockProofPrivateKey = \"XFfpaSbZq52HPy3WWwe1dXsZMiU1bQn8vQd34HNXkSZThevBWRn1\"; // WIF format\n\n// Public keys array with proper key types\nconst publicKeys = JSON.stringify([\n {\n id: 0,\n type: 0, // ECDSA_SECP256K1 = 0, BLS12_381 = 1, ECDSA_HASH160 = 2\n purpose: 0, // AUTHENTICATION = 0, ENCRYPTION = 1, DECRYPTION = 2, TRANSFER = 3, etc.\n securityLevel: 0, // MASTER = 0, CRITICAL = 1, HIGH = 2, MEDIUM = 3\n data: \"A5GzYHPIolbHkFrp5l+s9IvF2lWMuuuSu3oWZB8vWHNJ\", // Base64-encoded public key\n readOnly: false\n },\n {\n id: 1,\n type: 0,\n purpose: 0,\n securityLevel: 2,\n data: \"AnotherBase64EncodedPublicKeyHere\", // Base64-encoded public key\n readOnly: false\n }\n]);\n\nconst result = await sdk.identityCreate(assetLockProof, assetLockProofPrivateKey, publicKeys);" + "sdk_example": "// Asset lock proof is a hex-encoded JSON object\nconst assetLockProof = \"a9147d3b... (hex-encoded)\";\nconst assetLockProofPrivateKey = \"XFfpaSbZq52HPy3WWwe1dXsZMiU1bQn8vQd34HNXkSZThevBWRn1\"; // WIF format\n\n// Public keys array with proper key types and private keys for signing/hashing\nconst publicKeys = JSON.stringify([\n {\n id: 0,\n keyType: \"ECDSA_SECP256K1\",\n purpose: \"AUTHENTICATION\",\n securityLevel: \"MASTER\",\n data: \"A5GzYHPIolbHkFrp5l+s9IvF2lWMuuuSu3oWZB8vWHNJ\", // Base64-encoded public key\n readOnly: false,\n privateKeyWif: \"XBrZJKcW4ajWVNAU6yP87WQog6CjFnpbqyAKgNTZRqmhYvPgMNV2\"\n },\n {\n id: 1,\n keyType: \"ECDSA_HASH160\",\n purpose: \"AUTHENTICATION\",\n securityLevel: \"HIGH\",\n data: \"ripemd160hash20bytes1234\", // Base64-encoded 20-byte RIPEMD160 hash\n readOnly: false,\n // ECDSA_HASH160 keys produce empty signatures (not required/used for signing)\n }\n]);\n\nconst result = await sdk.identityCreate(assetLockProof, assetLockProofPrivateKey, publicKeys);" }, "identityTopUp": { "label": "Identity Top Up", diff --git a/packages/wasm-sdk/docs.html b/packages/wasm-sdk/docs.html index d2a533b9410..72df8131cee 100644 --- a/packages/wasm-sdk/docs.html +++ b/packages/wasm-sdk/docs.html @@ -1967,7 +1967,7 @@
Parameters:
Public Keys string (required) -
JSON array of public keys +
JSON array of public keys. Key requirements: ECDSA_SECP256K1 requires privateKeyHex or privateKeyWif for signing, BLS12_381 requires privateKeyHex for signing, ECDSA_HASH160 requires either the data field (base64-encoded 20-byte public key hash) or privateKeyHex (produces empty signatures). @@ -1977,23 +1977,25 @@
Example
const assetLockProof = "a9147d3b... (hex-encoded)"; const assetLockProofPrivateKey = "XFfpaSbZq52HPy3WWwe1dXsZMiU1bQn8vQd34HNXkSZThevBWRn1"; // WIF format -// Public keys array with proper key types +// Public keys array with proper key types and private keys for signing/hashing const publicKeys = JSON.stringify([ { id: 0, - type: 0, // ECDSA_SECP256K1 = 0, BLS12_381 = 1, ECDSA_HASH160 = 2 - purpose: 0, // AUTHENTICATION = 0, ENCRYPTION = 1, DECRYPTION = 2, TRANSFER = 3, etc. - securityLevel: 0, // MASTER = 0, CRITICAL = 1, HIGH = 2, MEDIUM = 3 + keyType: "ECDSA_SECP256K1", + purpose: "AUTHENTICATION", + securityLevel: "MASTER", data: "A5GzYHPIolbHkFrp5l+s9IvF2lWMuuuSu3oWZB8vWHNJ", // Base64-encoded public key - readOnly: false + readOnly: false, + privateKeyWif: "XBrZJKcW4ajWVNAU6yP87WQog6CjFnpbqyAKgNTZRqmhYvPgMNV2" }, { id: 1, - type: 0, - purpose: 0, - securityLevel: 2, - data: "AnotherBase64EncodedPublicKeyHere", // Base64-encoded public key - readOnly: false + keyType: "ECDSA_HASH160", + purpose: "AUTHENTICATION", + securityLevel: "HIGH", + data: "ripemd160hash20bytes1234", // Base64-encoded 20-byte RIPEMD160 hash + readOnly: false, + // ECDSA_HASH160 keys produce empty signatures (not required/used for signing) } ]); diff --git a/packages/wasm-sdk/index.html b/packages/wasm-sdk/index.html index 94d5cfb5438..d638e408697 100644 --- a/packages/wasm-sdk/index.html +++ b/packages/wasm-sdk/index.html @@ -3062,6 +3062,7 @@

Results

const seedPhrase = values.seedPhrase?.trim(); const identityIndex = parseInt(values.identityIndex || '0'); const keySelectionMode = values.keySelectionMode || 'default'; + const selectedKeyType = values.keyType || 'ECDSA_HASH160'; if (!seedPhrase || !validate_mnemonic(seedPhrase)) { throw new Error('Invalid seed phrase'); @@ -3089,10 +3090,11 @@

Results

// Create public key objects for the SDK // Pass private keys so the SDK can derive the correct public key data using DPP + // Use the selected key type for all keys publicKeys = [ { id: 0, - keyType: "ECDSA_HASH160", + keyType: selectedKeyType, purpose: "AUTHENTICATION", securityLevel: "MASTER", privateKeyHex: masterKey.private_key_hex, @@ -3100,7 +3102,7 @@

Results

}, { id: 1, - keyType: "ECDSA_HASH160", + keyType: selectedKeyType, purpose: "AUTHENTICATION", securityLevel: "HIGH", privateKeyHex: authKey.private_key_hex, @@ -3108,7 +3110,7 @@

Results

}, { id: 2, - keyType: "ECDSA_HASH160", + keyType: selectedKeyType, purpose: "TRANSFER", securityLevel: "CRITICAL", privateKeyHex: transferKey.private_key_hex, @@ -4293,6 +4295,7 @@

Results

const seedPhrase = dynamicInputs.querySelector('[name="seedPhrase"]')?.value?.trim(); const identityIndex = parseInt(dynamicInputs.querySelector('[name="identityIndex"]')?.value || '0'); const keySelectionMode = dynamicInputs.querySelector('[name="keySelectionMode"]')?.value || 'default'; + const selectedKeyType = dynamicInputs.querySelector('[name="keyType"]')?.value || 'ECDSA_HASH160'; const previewContainer = document.getElementById('keyPreviewContainer'); if (!seedPhrase || !previewContainer) return; @@ -4320,21 +4323,26 @@

Results

const transferKeyPath = `m/9'/${currentNetwork === 'mainnet' ? 5 : 1}'/5'/0'/0'/${identityIndex}'/2'`; const transferKey = derive_key_from_seed_with_path(seedPhrase, undefined, transferKeyPath, currentNetwork); + // Show signature requirement info + const signatureInfo = selectedKeyType === 'ECDSA_SECP256K1' + ? ' (Individual signatures required)' + : ' (No individual signatures required)'; + keysHtml += `
Master Key:
${masterKey.private_key_wif}
- Purpose: Authentication | Security: Master | Type: ECDSA_HASH160 + Purpose: Authentication | Security: Master | Type: ${selectedKeyType}${signatureInfo}
Key 1:
${authKey.private_key_wif}
- Purpose: Authentication | Security: High | Type: ECDSA_HASH160 + Purpose: Authentication | Security: High | Type: ${selectedKeyType}${signatureInfo}
Key 2:
${transferKey.private_key_wif}
- Purpose: Transfer | Security: Critical | Type: ECDSA_HASH160 + Purpose: Transfer | Security: Critical | Type: ${selectedKeyType}${signatureInfo}
`; @@ -4353,10 +4361,12 @@

Results

const seedInput = dynamicInputs.querySelector('[name="seedPhrase"]'); const indexInput = dynamicInputs.querySelector('[name="identityIndex"]'); const modeSelect = dynamicInputs.querySelector('[name="keySelectionMode"]'); + const keyTypeSelect = dynamicInputs.querySelector('[name="keyType"]'); if (seedInput) seedInput.addEventListener('input', updateKeyPreview); if (indexInput) indexInput.addEventListener('input', updateKeyPreview); if (modeSelect) modeSelect.addEventListener('change', updateKeyPreview); + if (keyTypeSelect) keyTypeSelect.addEventListener('change', updateKeyPreview); // Initial update updateKeyPreview(); diff --git a/packages/wasm-sdk/src/state_transitions/identity/mod.rs b/packages/wasm-sdk/src/state_transitions/identity/mod.rs index 995d43e94be..6538880d095 100644 --- a/packages/wasm-sdk/src/state_transitions/identity/mod.rs +++ b/packages/wasm-sdk/src/state_transitions/identity/mod.rs @@ -9,7 +9,7 @@ use dash_sdk::dpp::state_transition::identity_credit_transfer_transition::Identi use dash_sdk::dpp::state_transition::identity_credit_transfer_transition::methods::IdentityCreditTransferTransitionMethodsV0; use dash_sdk::platform::transition::broadcast::BroadcastStateTransition; use dash_sdk::platform::Fetch; -use simple_signer::SingleKeySigner; +use simple_signer::{SingleKeySigner, signer::SimpleSigner}; use wasm_bindgen::prelude::*; use wasm_bindgen::JsValue; use js_sys; @@ -27,7 +27,17 @@ impl WasmSdk { /// /// * `asset_lock_proof` - The asset lock proof (transaction hex) /// * `asset_lock_proof_private_key` - The private key that controls the asset lock - /// * `public_keys` - JSON array of public keys to add to the identity + /// * `public_keys` - JSON array of public keys to add to the identity. Each key object requirements: + /// - ECDSA_SECP256K1: Requires `privateKeyHex` or `privateKeyWif` for signing + /// - BLS12_381: Requires `privateKeyHex` for signing (WIF format not supported) + /// - ECDSA_HASH160: Accepts either `privateKeyHex` (to derive hash) or `data` field (base64-encoded 20-byte hash) + /// + /// # Implementation Notes + /// + /// This function uses SimpleSigner to provide individual signatures for each public key as required. + /// Each ECDSA_SECP256K1 key will be signed with its corresponding private key (from privateKeyHex or privateKeyWif), + /// and each BLS12_381 key will be signed with its corresponding private key (from privateKeyHex only), + /// ensuring unique signatures per key as required by DPP validation. /// /// # Returns /// @@ -80,8 +90,9 @@ impl WasmSdk { let keys_array = keys_data.as_array() .ok_or_else(|| JsValue::from_str("public_keys must be a JSON array"))?; - // Create identity public keys + // Create identity public keys and collect private keys for signing let mut identity_public_keys = std::collections::BTreeMap::new(); + let mut signer = SimpleSigner::default(); let mut key_id = 0u32; for key_data in keys_array { @@ -122,31 +133,120 @@ impl WasmSdk { _ => SecurityLevel::HIGH }; - // Check if privateKeyHex is provided - if so, derive public key data from it - let public_key_data = if let Some(private_key_hex) = key_data["privateKeyHex"].as_str() { - // Decode private key from hex - let private_key_bytes = hex::decode(private_key_hex) - .map_err(|e| JsValue::from_str(&format!("Invalid private key hex: {}", e)))?; - - if private_key_bytes.len() != 32 { - return Err(JsValue::from_str(&format!("Private key must be 32 bytes, got {}", private_key_bytes.len()))); + // Handle key data based on key type + let (public_key_data, private_key_bytes) = match key_type { + KeyType::ECDSA_HASH160 => { + // Derive HASH160 data from the private key if provided + if let Some(private_key_hex) = key_data["privateKeyHex"].as_str() { + // Decode private key from hex + let bytes = hex::decode(private_key_hex) + .map_err(|e| JsValue::from_str(&format!("Invalid private key hex: {}", e)))?; + + if bytes.len() != 32 { + return Err(JsValue::from_str(&format!( + "Private key must be 32 bytes, got {}", + bytes.len() + ))); + } + + let mut private_key_array = [0u8; 32]; + private_key_array.copy_from_slice(&bytes); + + // Derive HASH160 public key data from private key using network + let derived_data = key_type + .public_key_data_from_private_key_data(&private_key_array, self.network()) + .map_err(|e| { + JsValue::from_str(&format!( + "Failed to derive ECDSA_HASH160 public key data: {}", + e + )) + })?; + + // HASH160 keys are not used for signing during identity creation + (derived_data, [0u8; 32]) + } else if let Some(data_str) = key_data["data"].as_str() { + let key_data_bytes = dash_sdk::dpp::dashcore::base64::decode(data_str) + .map_err(|e| JsValue::from_str(&format!("Invalid base64 key data: {}", e)))?; + + // Enforce correct HASH160 size (20 bytes). + if key_data_bytes.len() != 20 { + return Err(JsValue::from_str(&format!( + "ECDSA_HASH160 key data must be 20 bytes, got {}", + key_data_bytes.len() + ))); + } + + (key_data_bytes, [0u8; 32]) + } else { + return Err(JsValue::from_str( + "ECDSA_HASH160 requires either 'privateKeyHex' to derive from or 'data' (base64-encoded 20-byte hash)", + )); + } + }, + KeyType::ECDSA_SECP256K1 => { + // For ECDSA signing keys, support both hex and WIF formats + let private_key_bytes = if let Some(private_key_hex) = key_data["privateKeyHex"].as_str() { + // Decode private key from hex + let bytes = hex::decode(private_key_hex) + .map_err(|e| JsValue::from_str(&format!("Invalid private key hex: {}", e)))?; + + if bytes.len() != 32 { + return Err(JsValue::from_str(&format!("Private key must be 32 bytes, got {}", bytes.len()))); + } + + let mut private_key_array = [0u8; 32]; + private_key_array.copy_from_slice(&bytes); + private_key_array + } else if let Some(private_key_wif) = key_data["privateKeyWif"].as_str() { + // Parse WIF format private key + let private_key = PrivateKey::from_wif(private_key_wif) + .map_err(|e| JsValue::from_str(&format!("Invalid WIF private key: {}", e)))?; + private_key.inner.secret_bytes() + } else { + return Err(JsValue::from_str("ECDSA_SECP256K1 keys require either privateKeyHex or privateKeyWif")); + }; + + // Derive public key data from private key + let public_key_data = key_type.public_key_data_from_private_key_data( + &private_key_bytes, + self.network() + ).map_err(|e| JsValue::from_str(&format!("Failed to derive ECDSA_SECP256K1 public key data: {}", e)))?; + + (public_key_data, private_key_bytes) + }, + KeyType::BLS12_381 => { + // BLS12_381 keys only support hex format (WIF is not valid for BLS keys) + if key_data["privateKeyWif"].is_string() { + return Err(JsValue::from_str("BLS12_381 keys do not support WIF format, use privateKeyHex only")); + } + + let private_key_bytes = if let Some(private_key_hex) = key_data["privateKeyHex"].as_str() { + // Decode private key from hex + let bytes = hex::decode(private_key_hex) + .map_err(|e| JsValue::from_str(&format!("Invalid private key hex: {}", e)))?; + + if bytes.len() != 32 { + return Err(JsValue::from_str(&format!("Private key must be 32 bytes, got {}", bytes.len()))); + } + + let mut private_key_array = [0u8; 32]; + private_key_array.copy_from_slice(&bytes); + private_key_array + } else { + return Err(JsValue::from_str("BLS12_381 keys require privateKeyHex")); + }; + + // Derive public key data from private key + let public_key_data = key_type.public_key_data_from_private_key_data( + &private_key_bytes, + self.network() + ).map_err(|e| JsValue::from_str(&format!("Failed to derive BLS12_381 public key data: {}", e)))?; + + (public_key_data, private_key_bytes) + }, + _ => { + return Err(JsValue::from_str(&format!("Unsupported key type for identity creation: {}", key_type_str))); } - - let mut private_key_array = [0u8; 32]; - private_key_array.copy_from_slice(&private_key_bytes); - - // Use DPP's built-in method to get the correct public key data for the key type - // Use network from SDK configuration - key_type.public_key_data_from_private_key_data( - &private_key_array, - self.network() - ).map_err(|e| JsValue::from_str(&format!("Failed to derive public key data: {}", e)))? - } else if let Some(data_str) = key_data["data"].as_str() { - // Fall back to using provided data (base64 encoded) - dash_sdk::dpp::dashcore::base64::decode(data_str) - .map_err(|e| JsValue::from_str(&format!("Invalid base64 key data: {}", e)))? - } else { - return Err(JsValue::from_str("Either privateKeyHex or data must be provided")); }; // Create the identity public key @@ -162,6 +262,11 @@ impl WasmSdk { disabled_at: None, }); + // Add the public key and its private key to the signer (only for signing key types) + if key_type != KeyType::ECDSA_HASH160 { + signer.add_key(public_key.clone(), private_key_bytes); + } + identity_public_keys.insert(key_id, public_key); key_id += 1; } @@ -175,13 +280,8 @@ impl WasmSdk { revision: 0, }); - // Create signer from asset lock proof private key using SDK's network configuration - let signer = SingleKeySigner::from_string(&asset_lock_proof_private_key, self.network()) - .map_err(|e| { - let error_msg = format!("Invalid private key: {}", e); - web_sys::console::error_1(&JsValue::from_str(&error_msg)); - JsValue::from_str(&error_msg) - })?; + // Use the SimpleSigner we built with all the identity keys + // The signer now contains all private keys for signing each public key individually // Put identity to platform and wait let created_identity = match identity diff --git a/packages/wasm-sdk/test/state-transitions.test.mjs b/packages/wasm-sdk/test/state-transitions.test.mjs index 9f8fb34c371..9366a73cba4 100644 --- a/packages/wasm-sdk/test/state-transitions.test.mjs +++ b/packages/wasm-sdk/test/state-transitions.test.mjs @@ -20,7 +20,8 @@ if (!global.crypto) { } // Import WASM SDK -import init, * as wasmSdk from '../pkg/wasm_sdk.js'; +import init, { generate_key_pair } from '../pkg/wasm_sdk.js'; +import * as wasmSdk from '../pkg/wasm_sdk.js'; // Initialize WASM console.log('Initializing WASM SDK...'); @@ -81,6 +82,171 @@ await test('identity_create - requires funding', async () => { } }); +await test('identity_create with all SECP256K1 keys (common scenario)', async () => { + try { + // Generate unique keys for testing (1 asset lock + 3 identity keys) + const assetLockKey = generate_key_pair("testnet"); + const secp256k1Key1 = generate_key_pair("testnet"); + const secp256k1Key2 = generate_key_pair("testnet"); + const secp256k1Key3 = generate_key_pair("testnet"); + + // Mock asset lock proof + const mockAssetLockProof = JSON.stringify({ + coreChainLockedHeight: 1000000, + outPoint: "0000000000000000000000000000000000000000000000000000000000000000:0" + }); + + // Create public keys array with all SECP256K1 keys + const publicKeys = JSON.stringify([ + { + keyType: "ECDSA_SECP256K1", + purpose: "AUTHENTICATION", + securityLevel: "MASTER", + privateKeyHex: secp256k1Key1.private_key_hex + }, + { + keyType: "ECDSA_SECP256K1", + purpose: "AUTHENTICATION", + securityLevel: "CRITICAL", + privateKeyHex: secp256k1Key2.private_key_hex + }, + { + keyType: "ECDSA_SECP256K1", + purpose: "ENCRYPTION", + securityLevel: "HIGH", + privateKeyHex: secp256k1Key3.private_key_hex + } + ]); + + const result = await sdk.identityCreate( + mockAssetLockProof, + assetLockKey.private_key_wif, + publicKeys + ); + throw new Error('Should fail with mock data'); + } catch (error) { + const errorMessage = error.message || error.toString() || 'Unknown error'; + if (errorMessage.includes('Should fail')) { + throw error; + } + // Check that it's NOT a signature verification error + if (errorMessage.includes('signature failed verification')) { + throw new Error('SIGNATURE VERIFICATION ERROR - SimpleSigner may not be working correctly'); + } + console.log(' Expected error with mock data (not signature error)'); + } +}); + +await test('identity_create with mixed key types (SECP256K1 and HASH160)', async () => { + try { + // Generate unique keys for testing (1 asset lock + 2 SECP256K1 + 1 HASH160) + const assetLockKey = generate_key_pair("testnet"); + const secp256k1Key1 = generate_key_pair("testnet"); + const secp256k1Key2 = generate_key_pair("testnet"); + const hash160Key = generate_key_pair("testnet"); + + // Mock asset lock proof + const mockAssetLockProof = JSON.stringify({ + coreChainLockedHeight: 1000000, + outPoint: "0000000000000000000000000000000000000000000000000000000000000001:0" + }); + + // Create mixed public keys array + const publicKeys = JSON.stringify([ + { + keyType: "ECDSA_SECP256K1", + purpose: "AUTHENTICATION", + securityLevel: "MASTER", + privateKeyHex: secp256k1Key1.private_key_hex + }, + { + keyType: "ECDSA_SECP256K1", + purpose: "AUTHENTICATION", + securityLevel: "CRITICAL", + privateKeyHex: secp256k1Key2.private_key_hex + }, + { + keyType: "ECDSA_HASH160", + purpose: "TRANSFER", + securityLevel: "HIGH", + privateKeyHex: hash160Key.private_key_hex + } + ]); + + const result = await sdk.identityCreate( + mockAssetLockProof, + assetLockKey.private_key_wif, + publicKeys + ); + throw new Error('Should fail with mock data'); + } catch (error) { + const errorMessage = error.message || error.toString() || 'Unknown error'; + if (errorMessage.includes('Should fail')) { + throw error; + } + // Check that it's NOT a signature verification error + if (errorMessage.includes('signature failed verification')) { + throw new Error('SIGNATURE VERIFICATION ERROR - SimpleSigner may not be working correctly'); + } + console.log(' Expected error with mock data (not signature error)'); + } +}); + +await test('identity_create with only HASH160 keys', async () => { + try { + // Generate unique keys for testing (1 asset lock + 3 HASH160) + const assetLockKey = generate_key_pair("testnet"); + const hash160Key1 = generate_key_pair("testnet"); + const hash160Key2 = generate_key_pair("testnet"); + const hash160Key3 = generate_key_pair("testnet"); + + // Mock asset lock proof + const mockAssetLockProof = JSON.stringify({ + coreChainLockedHeight: 1000000, + outPoint: "0000000000000000000000000000000000000000000000000000000000000002:0" + }); + + // Create public keys array with all HASH160 keys + const publicKeys = JSON.stringify([ + { + keyType: "ECDSA_HASH160", + purpose: "AUTHENTICATION", + securityLevel: "MASTER", + privateKeyHex: hash160Key1.private_key_hex + }, + { + keyType: "ECDSA_HASH160", + purpose: "AUTHENTICATION", + securityLevel: "CRITICAL", + privateKeyHex: hash160Key2.private_key_hex + }, + { + keyType: "ECDSA_HASH160", + purpose: "TRANSFER", + securityLevel: "HIGH", + privateKeyHex: hash160Key3.private_key_hex + } + ]); + + const result = await sdk.identityCreate( + mockAssetLockProof, + assetLockKey.private_key_wif, + publicKeys + ); + throw new Error('Should fail with mock data'); + } catch (error) { + const errorMessage = error.message || error.toString() || 'Unknown error'; + if (errorMessage.includes('Should fail')) { + throw error; + } + // Check that it's NOT a signature verification error + if (errorMessage.includes('signature failed verification')) { + throw new Error('SIGNATURE VERIFICATION ERROR - SimpleSigner may not be working correctly'); + } + console.log(' Expected error with mock data (not signature error)'); + } +}); + await test('identity_update - requires existing identity', async () => { try { const updateData = JSON.stringify({ @@ -144,18 +310,6 @@ await test('identity_withdraw - requires balance', async () => { } }); -await test('identity_put - causes panic in WASM', async () => { - try { - const result = await wasmSdk.identity_put(sdk); - throw new Error('Should have panicked'); - } catch (error) { - if (error.message.includes('Should have panicked')) { - throw error; - } - console.log(' Expected panic (known issue)'); - } -}); - // Document State Transitions describe('Document State Transitions');