@@ -135,7 +135,7 @@ impl NearKmsClient {
135135 worker_public_key : worker_public_key. to_string ( ) ,
136136 } ;
137137
138- // Call the contract method using near-api
138+ // Call the contract method
139139 let execution_result = self
140140 . kms_contract
141141 . call_function ( "request_kms_root_key" , args)
@@ -145,29 +145,15 @@ impl NearKmsClient {
145145 . await
146146 . context ( "Failed to call KMS contract" ) ?;
147147
148- // Assert that the transaction succeeded and get receipt outcomes
149- // Note: assert_success() may consume the result, so we need to handle this carefully
150- let receipt_outcomes = execution_result. receipt_outcomes ( ) . to_vec ( ) ;
151- execution_result. assert_success ( ) ;
152-
153148 // Extract the return value from the transaction result
154- // The return value comes via Promise callback in the receipt outcomes
155- // We need to find the return value in one of the receipt outcomes
156- // TODO: Implement proper extraction based on the actual near-api API
157- // The return value from Promise callbacks needs to be extracted from the appropriate receipt outcome
158- // This may require checking the receipt IDs and following the Promise chain, or using
159- // a helper method if the API provides one
160-
161- // For now, return an error indicating this needs implementation
162- // The actual implementation will depend on how the near-api library exposes
163- // the return values from Promise callbacks in receipt outcomes
164- anyhow:: bail!(
165- "MPC response extraction needs proper implementation. \
166- The response comes via Promise callback in receipt outcomes. \
167- Please check the near-api documentation for the correct way to extract return values from ExecutionFinalResult. \
168- Receipt outcomes count: {}",
169- receipt_outcomes. len( )
170- )
149+ let execution_success = execution_result
150+ . into_result ( )
151+ . context ( "Transaction execution failed" ) ?;
152+ let ckd_response: CkdResponse = execution_success
153+ . json ( )
154+ . context ( "Failed to deserialize CkdResponse from transaction return value" ) ?;
155+
156+ Ok ( ckd_response)
171157 }
172158
173159 /// Parse MPC response from transaction receipt value
@@ -314,3 +300,182 @@ pub fn near_public_key_to_report_data(
314300
315301 Ok ( report_data)
316302}
303+
304+ #[ cfg( test) ]
305+ mod tests {
306+ use super :: * ;
307+ use near_crypto:: PublicKey ;
308+ use sha3:: { Digest , Sha3_384 } ;
309+
310+ #[ test]
311+ fn test_generate_near_implicit_signer ( ) {
312+ // Test that we can generate a signer
313+ let signer = generate_near_implicit_signer ( ) . expect ( "Should generate a signer" ) ;
314+
315+ // Verify the signer has valid fields
316+ assert ! ( !signer. account_id. to_string( ) . is_empty( ) ) ;
317+ assert_eq ! ( signer. account_id. to_string( ) . len( ) , 64 ) ; // Hex-encoded 32-byte public key = 64 chars
318+
319+ // Verify the public key matches the account ID derivation
320+ let public_key_bytes = match & signer. public_key {
321+ PublicKey :: ED25519 ( pk) => pk. as_byte_slice ( ) ,
322+ _ => panic ! ( "Expected ED25519 key" ) ,
323+ } ;
324+ let expected_account_id = hex:: encode ( public_key_bytes) ;
325+ assert_eq ! ( signer. account_id. to_string( ) , expected_account_id) ;
326+
327+ // Verify the signer can be used (secret key and public key match)
328+ let derived_public_key = signer. secret_key . public_key ( ) ;
329+ assert_eq ! ( signer. public_key, derived_public_key) ;
330+ }
331+
332+ #[ test]
333+ fn test_generate_multiple_signers_are_different ( ) {
334+ // Generate multiple signers and verify they're different
335+ let signer1 = generate_near_implicit_signer ( ) . expect ( "Should generate signer 1" ) ;
336+ let signer2 = generate_near_implicit_signer ( ) . expect ( "Should generate signer 2" ) ;
337+ let signer3 = generate_near_implicit_signer ( ) . expect ( "Should generate signer 3" ) ;
338+
339+ // All should have different account IDs
340+ assert_ne ! ( signer1. account_id, signer2. account_id) ;
341+ assert_ne ! ( signer2. account_id, signer3. account_id) ;
342+ assert_ne ! ( signer1. account_id, signer3. account_id) ;
343+
344+ // All should have different secret keys
345+ assert_ne ! (
346+ signer1. secret_key. to_string( ) ,
347+ signer2. secret_key. to_string( )
348+ ) ;
349+ assert_ne ! (
350+ signer2. secret_key. to_string( ) ,
351+ signer3. secret_key. to_string( )
352+ ) ;
353+ }
354+
355+ #[ test]
356+ fn test_near_public_key_to_report_data_format ( ) {
357+ // Generate a test public key
358+ let secret_key = near_crypto:: SecretKey :: from_random ( near_crypto:: KeyType :: ED25519 ) ;
359+ let public_key = secret_key. public_key ( ) ;
360+
361+ // Convert to report_data
362+ let report_data = near_public_key_to_report_data ( & public_key)
363+ . expect ( "Should convert public key to report_data" ) ;
364+
365+ // Verify size
366+ assert_eq ! ( report_data. len( ) , 64 ) ;
367+
368+ // Verify version bytes (first 2 bytes should be [0, 1] for version 1)
369+ assert_eq ! ( report_data[ 0 ..2 ] , [ 0 , 1 ] ) ;
370+
371+ // Verify hash bytes are non-zero (bytes 2-50 should contain the hash)
372+ let hash_section = & report_data[ 2 ..50 ] ;
373+ assert ! ( !hash_section. iter( ) . all( |& b| b == 0 ) , "Hash should not be all zeros" ) ;
374+
375+ // Verify padding bytes are zero (bytes 50-64 should be zero)
376+ let padding_section = & report_data[ 50 ..64 ] ;
377+ assert ! ( padding_section. iter( ) . all( |& b| b == 0 ) , "Padding should be zeros" ) ;
378+ }
379+
380+ #[ test]
381+ fn test_near_public_key_to_report_data_hash_correctness ( ) {
382+ // Generate a test public key
383+ let secret_key = near_crypto:: SecretKey :: from_random ( near_crypto:: KeyType :: ED25519 ) ;
384+ let public_key = secret_key. public_key ( ) ;
385+
386+ // Convert to report_data
387+ let report_data = near_public_key_to_report_data ( & public_key)
388+ . expect ( "Should convert public key to report_data" ) ;
389+
390+ // Manually compute the expected hash
391+ let public_key_bytes = match & public_key {
392+ PublicKey :: ED25519 ( pk) => pk. as_byte_slice ( ) ,
393+ _ => panic ! ( "Expected ED25519 key" ) ,
394+ } ;
395+
396+ let mut hasher = Sha3_384 :: new ( ) ;
397+ hasher. update ( & public_key_bytes[ 1 ..] ) ; // Skip first byte (curve type)
398+ let expected_hash: [ u8 ; 48 ] = hasher. finalize ( ) . into ( ) ;
399+
400+ // Verify the hash matches
401+ assert_eq ! ( & report_data[ 2 ..50 ] , & expected_hash) ;
402+ }
403+
404+ #[ test]
405+ fn test_near_public_key_to_report_data_deterministic ( ) {
406+ // Generate a test public key
407+ let secret_key = near_crypto:: SecretKey :: from_random ( near_crypto:: KeyType :: ED25519 ) ;
408+ let public_key = secret_key. public_key ( ) ;
409+
410+ // Convert multiple times - should be deterministic
411+ let report_data1 = near_public_key_to_report_data ( & public_key)
412+ . expect ( "Should convert public key to report_data" ) ;
413+ let report_data2 = near_public_key_to_report_data ( & public_key)
414+ . expect ( "Should convert public key to report_data" ) ;
415+ let report_data3 = near_public_key_to_report_data ( & public_key)
416+ . expect ( "Should convert public key to report_data" ) ;
417+
418+ // All should be identical
419+ assert_eq ! ( report_data1, report_data2) ;
420+ assert_eq ! ( report_data2, report_data3) ;
421+ }
422+
423+ #[ test]
424+ fn test_near_public_key_to_report_data_different_keys_different_output ( ) {
425+ // Generate two different public keys
426+ let secret_key1 = near_crypto:: SecretKey :: from_random ( near_crypto:: KeyType :: ED25519 ) ;
427+ let public_key1 = secret_key1. public_key ( ) ;
428+
429+ let secret_key2 = near_crypto:: SecretKey :: from_random ( near_crypto:: KeyType :: ED25519 ) ;
430+ let public_key2 = secret_key2. public_key ( ) ;
431+
432+ // Convert both to report_data
433+ let report_data1 = near_public_key_to_report_data ( & public_key1)
434+ . expect ( "Should convert public key 1 to report_data" ) ;
435+ let report_data2 = near_public_key_to_report_data ( & public_key2)
436+ . expect ( "Should convert public key 2 to report_data" ) ;
437+
438+ // They should be different
439+ assert_ne ! ( report_data1, report_data2) ;
440+ }
441+
442+ #[ test]
443+ fn test_parse_ckd_response_valid_json ( ) {
444+ // Create valid CkdResponse JSON
445+ let json_data = r#"{
446+ "big_y": "ed25519:test_y_key",
447+ "big_c": "ed25519:test_c_key"
448+ }"# ;
449+
450+ // Parse the response directly
451+ let result: Result < CkdResponse , _ > = serde_json:: from_str ( json_data) ;
452+ assert ! ( result. is_ok( ) , "Should parse valid JSON" ) ;
453+
454+ let ckd_response = result. unwrap ( ) ;
455+ // Verify the response was parsed correctly by checking the string representation
456+ // (Bls12381G1PublicKey wraps a String, but we can't access it directly)
457+ // We can at least verify the struct was created successfully
458+ let _ = ckd_response. big_y ;
459+ let _ = ckd_response. big_c ;
460+ }
461+
462+ #[ test]
463+ fn test_parse_ckd_response_invalid_json ( ) {
464+ use crate :: ckd:: CkdResponse ;
465+
466+ // Try to parse invalid JSON
467+ let invalid_json = "{ invalid json }" ;
468+ let result: Result < CkdResponse , _ > = serde_json:: from_str ( invalid_json) ;
469+ assert ! ( result. is_err( ) , "Should fail to parse invalid JSON" ) ;
470+ }
471+
472+ #[ test]
473+ fn test_parse_ckd_response_missing_fields ( ) {
474+ use crate :: ckd:: CkdResponse ;
475+
476+ // Try to parse JSON with missing fields
477+ let incomplete_json = r#"{"big_y": "ed25519:test_y_key"}"# ;
478+ let result: Result < CkdResponse , _ > = serde_json:: from_str ( incomplete_json) ;
479+ assert ! ( result. is_err( ) , "Should fail to parse incomplete JSON" ) ;
480+ }
481+ }
0 commit comments