From 58fe1402f6fe7a294cf0fbfad54718f309874a74 Mon Sep 17 00:00:00 2001 From: Eran Rundstein Date: Thu, 7 Nov 2024 11:21:17 -0800 Subject: [PATCH 1/2] mobilecoind: add tx_out_view_key_match api --- mobilecoind/api/proto/mobilecoind_api.proto | 20 +++++ mobilecoind/src/service.rs | 94 ++++++++++++++++++++- 2 files changed, 113 insertions(+), 1 deletion(-) diff --git a/mobilecoind/api/proto/mobilecoind_api.proto b/mobilecoind/api/proto/mobilecoind_api.proto index b7267591ab..f15159ab46 100644 --- a/mobilecoind/api/proto/mobilecoind_api.proto +++ b/mobilecoind/api/proto/mobilecoind_api.proto @@ -33,6 +33,7 @@ service MobilecoindAPI { rpc GetPublicAddress (GetPublicAddressRequest) returns (GetPublicAddressResponse) {} rpc GetShortAddressHash (GetShortAddressHashRequest) returns (GetShortAddressHashResponse) {} rpc ValidateAuthenticatedSenderMemo (ValidateAuthenticatedSenderMemoRequest) returns (ValidateAuthenticatedSenderMemoResponse) {} + rpc TxOutViewKeyMatch (TxOutViewKeyMatchRequest) returns (TxOutViewKeyMatchResponse) {} // b58 Codes rpc ParseRequestCode (ParseRequestCodeRequest) returns (ParseRequestCodeResponse) {} @@ -509,6 +510,25 @@ message ValidateAuthenticatedSenderMemoResponse { bool success = 1; } +message TxOutViewKeyMatchRequest { + external.TxOut txo = 1; + external.RistrettoPrivate view_private_key = 2; +} + +message TxOutViewKeyMatchResponse { + // Whether the tx out belongs to the provided view private key. + bool matched = 1; + + // The value of the tx out, only valid if matched is true. + uint64 value = 2; + + // The token_id of the tx out, only valid if matched is true. + uint64 token_id = 3; + + // The tx out shared secret, only valid if matched is true. + external.CompressedRistretto shared_secret = 4; +} + // // b58 Codes // diff --git a/mobilecoind/src/service.rs b/mobilecoind/src/service.rs index 0a1a59b58b..d6f5ff20b5 100644 --- a/mobilecoind/src/service.rs +++ b/mobilecoind/src/service.rs @@ -30,7 +30,7 @@ use mc_common::{ }; use mc_connection::{BlockInfo, BlockchainConnection, UserTxConnection}; use mc_core::slip10::Slip10KeyGenerator; -use mc_crypto_keys::{CompressedRistrettoPublic, RistrettoPublic}; +use mc_crypto_keys::{CompressedRistrettoPublic, RistrettoPrivate, RistrettoPublic}; use mc_fog_report_validation::FogPubkeyResolver; use mc_ledger_db::{Error as LedgerError, Ledger, LedgerDB}; use mc_ledger_sync::{NetworkState, PollingNetworkState}; @@ -584,6 +584,34 @@ impl Result { + let tx_out = TxOut::try_from(request.get_txo()) + .map_err(|err| rpc_internal_error("tx_out.try_from", err, &self.logger))?; + let view_private_key = RistrettoPrivate::try_from(request.get_view_private_key()) + .map_err(|err| rpc_invalid_arg_error("view_private_key.try_from", err, &self.logger))?; + + match tx_out.view_key_match(&view_private_key) { + Ok((amount, shared_secret)) => { + let shared_secret: mc_api::external::CompressedRistretto = (&shared_secret).into(); + + Ok(api::TxOutViewKeyMatchResponse { + matched: true, + value: amount.value, + token_id: *amount.token_id, + shared_secret: Some(shared_secret).into(), + ..Default::default() + }) + } + Err(_) => Ok(api::TxOutViewKeyMatchResponse { + matched: false, + ..Default::default() + }), + } + } + fn parse_request_code_impl( &mut self, request: api::ParseRequestCodeRequest, @@ -2626,6 +2654,7 @@ build_api! { get_public_address GetPublicAddressRequest GetPublicAddressResponse get_public_address_impl, get_short_address_hash GetShortAddressHashRequest GetShortAddressHashResponse get_short_address_hash_impl, validate_authenticated_sender_memo ValidateAuthenticatedSenderMemoRequest ValidateAuthenticatedSenderMemoResponse validate_authenticated_sender_memo_impl, + tx_out_view_key_match TxOutViewKeyMatchRequest TxOutViewKeyMatchResponse tx_out_view_key_match_impl, // b58 codes parse_request_code ParseRequestCodeRequest ParseRequestCodeResponse parse_request_code_impl, @@ -3505,6 +3534,69 @@ mod test { assert!(!response.success); } + #[test_with_logger] + fn test_tx_out_view_key_match_impl(logger: Logger) { + let mut rng: StdRng = SeedableRng::from_seed([23u8; 32]); + + // no known recipient, 3 random recipients and no monitors. + let (mut ledger_db, _mobilecoind_db, client, _server, _server_conn_manager) = + get_testing_environment(BLOCK_VERSION, 3, &[], &[], logger, &mut rng); + + // Insert a block with a known recipient (this is block 4) + let recipient = AccountKey::random(&mut rng); + add_block_to_ledger( + &mut ledger_db, + BLOCK_VERSION, + &[recipient.default_subaddress()], + Amount::new(102030, TokenId::from(1)), + &[KeyImage::from(101)], + &mut rng, + ) + .unwrap(); + + // Get the block so we can test the matching. + let block = client + .get_block(&api::GetBlockRequest { + block: ledger_db.num_blocks().unwrap() - 1, + ..Default::default() + }) + .unwrap(); + + // The block should have a single tx out and we should be able to match it with + // the correct view private key. + let view_private_key = + mc_api::external::RistrettoPrivate::from(recipient.view_private_key()); + let resp = client + .tx_out_view_key_match(&api::TxOutViewKeyMatchRequest { + txo: Some(block.txos[0].clone()).into(), + view_private_key: Some(view_private_key).into(), + ..Default::default() + }) + .unwrap(); + + assert_eq!(resp.matched, true); + assert_eq!(resp.value, 102030); + assert_eq!(resp.token_id, 1); + assert_eq!(resp.get_shared_secret().data.len(), 32); + + // Try with an incorrect view private key + let view_private_key = mc_api::external::RistrettoPrivate::from( + AccountKey::random(&mut rng).view_private_key(), + ); + let resp = client + .tx_out_view_key_match(&api::TxOutViewKeyMatchRequest { + txo: Some(block.txos[0].clone()).into(), + view_private_key: Some(view_private_key).into(), + ..Default::default() + }) + .unwrap(); + + assert_eq!(resp.matched, false); + assert_eq!(resp.value, 0); + assert_eq!(resp.token_id, 0); + assert_eq!(resp.get_shared_secret().data.len(), 0); + } + #[test_with_logger] fn test_vectors_validate_authenticated_sender_memo_impl(logger: Logger) { // In this test, we take an actual TxOut generated by signal, at block version From b93d28b0721ac6c848788d340f872d6e15f17982 Mon Sep 17 00:00:00 2001 From: Eran Rundstein Date: Tue, 12 Nov 2024 16:32:39 -0800 Subject: [PATCH 2/2] lint --- mobilecoind/src/service.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobilecoind/src/service.rs b/mobilecoind/src/service.rs index d6f5ff20b5..f6e5a6740b 100644 --- a/mobilecoind/src/service.rs +++ b/mobilecoind/src/service.rs @@ -3574,7 +3574,7 @@ mod test { }) .unwrap(); - assert_eq!(resp.matched, true); + assert!(resp.matched); assert_eq!(resp.value, 102030); assert_eq!(resp.token_id, 1); assert_eq!(resp.get_shared_secret().data.len(), 32); @@ -3591,7 +3591,7 @@ mod test { }) .unwrap(); - assert_eq!(resp.matched, false); + assert!(!resp.matched); assert_eq!(resp.value, 0); assert_eq!(resp.token_id, 0); assert_eq!(resp.get_shared_secret().data.len(), 0);