Skip to content

Commit e2f5a3a

Browse files
fix: parse execution result
1 parent f5bc5ff commit e2f5a3a

File tree

1 file changed

+188
-23
lines changed

1 file changed

+188
-23
lines changed

kms/src/near_kms_client.rs

Lines changed: 188 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)