From 2ef8902c825ebda7699f423fcdc7dff755a9488c Mon Sep 17 00:00:00 2001 From: Terry Mancey Date: Thu, 16 Jun 2022 12:01:56 -0500 Subject: [PATCH] Brave Ads failed confirmations should not backoff if payment tokens are not created or not ready --- .../account/confirmations/confirmations.cc | 10 ++- .../account/confirmations/confirmations.h | 3 +- .../redeem_unblinded_token.cc | 66 ++++++++++------ .../redeem_unblinded_token.h | 3 +- .../redeem_unblinded_token_delegate.h | 6 +- .../redeem_unblinded_token_delegate_mock.h | 4 +- .../redeem_unblinded_token_unittest.cc | 77 ++++++++++++++++--- 7 files changed, 130 insertions(+), 39 deletions(-) diff --git a/vendor/bat-native-ads/src/bat/ads/internal/account/confirmations/confirmations.cc b/vendor/bat-native-ads/src/bat/ads/internal/account/confirmations/confirmations.cc index 66207412047d..bfd1e484107a 100644 --- a/vendor/bat-native-ads/src/bat/ads/internal/account/confirmations/confirmations.cc +++ b/vendor/bat-native-ads/src/bat/ads/internal/account/confirmations/confirmations.cc @@ -277,7 +277,8 @@ void Confirmations::OnDidRedeemUnblindedToken( ->get_unblinded_payment_tokens() ->TokenExists(unblinded_payment_token)) { BLOG(1, "Unblinded payment token is a duplicate"); - OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ false); + OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ false, + /* should_backoff */ false); return; } @@ -312,7 +313,8 @@ void Confirmations::OnDidRedeemUnblindedToken( void Confirmations::OnFailedToRedeemUnblindedToken( const ConfirmationInfo& confirmation, - const bool should_retry) { + const bool should_retry, + const bool should_backoff) { BLOG(1, "Failed to redeem unblinded token for " << confirmation.ad_type << " with confirmation id " << confirmation.id << ", transaction id " @@ -332,6 +334,10 @@ void Confirmations::OnFailedToRedeemUnblindedToken( delegate_->OnFailedToConfirm(confirmation); } + if (!should_backoff) { + StopRetrying(); + } + ProcessRetryQueue(); } diff --git a/vendor/bat-native-ads/src/bat/ads/internal/account/confirmations/confirmations.h b/vendor/bat-native-ads/src/bat/ads/internal/account/confirmations/confirmations.h index 479ffb8dfc90..42e295d06ee0 100644 --- a/vendor/bat-native-ads/src/bat/ads/internal/account/confirmations/confirmations.h +++ b/vendor/bat-native-ads/src/bat/ads/internal/account/confirmations/confirmations.h @@ -73,7 +73,8 @@ class Confirmations final : public RedeemUnblindedTokenDelegate { const privacy::UnblindedPaymentTokenInfo& unblinded_payment_token) override; void OnFailedToRedeemUnblindedToken(const ConfirmationInfo& confirmation, - const bool should_retry) override; + const bool should_retry, + const bool should_backoff) override; raw_ptr delegate_ = nullptr; diff --git a/vendor/bat-native-ads/src/bat/ads/internal/account/utility/redeem_unblinded_token/redeem_unblinded_token.cc b/vendor/bat-native-ads/src/bat/ads/internal/account/utility/redeem_unblinded_token/redeem_unblinded_token.cc index f05f368b51ff..d5bd5022aa02 100644 --- a/vendor/bat-native-ads/src/bat/ads/internal/account/utility/redeem_unblinded_token/redeem_unblinded_token.cc +++ b/vendor/bat-native-ads/src/bat/ads/internal/account/utility/redeem_unblinded_token/redeem_unblinded_token.cc @@ -51,7 +51,8 @@ void RedeemUnblindedToken::Redeem(const ConfirmationInfo& confirmation) { if (ShouldRewardUser() && !HasIssuers()) { BLOG(1, "Failed to redeem unblinded token due to missing issuers"); - OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true); + OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true, + /* should_backoff */ true); return; } @@ -100,7 +101,8 @@ void RedeemUnblindedToken::OnCreateConfirmation( return; } else if (url_response.status_code == net::kHttpUpgradeRequired) { BLOG(1, "Failed to create confirmation as a browser upgrade is required"); - OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ false); + OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ false, + /* should_backoff */ false); return; } @@ -143,29 +145,35 @@ void RedeemUnblindedToken::OnFetchPaymentToken( ConfirmationInfo new_confirmation = confirmation; new_confirmation.was_created = false; - OnFailedToRedeemUnblindedToken(new_confirmation, /* should_retry */ true); + OnFailedToRedeemUnblindedToken(new_confirmation, /* should_retry */ true, + /* should_backoff */ false); return; } else if (url_response.status_code == net::HTTP_BAD_REQUEST) { BLOG(1, "Credential is invalid"); - OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ false); + OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ false, + /* should_backoff */ false); return; } else if (url_response.status_code == net::HTTP_ACCEPTED) { BLOG(1, "Payment token is not ready"); - OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true); + OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true, + /* should_backoff */ false); return; } else if (url_response.status_code == net::kHttpUpgradeRequired) { BLOG(1, "Failed to fetch payment token as a browser upgrade is required"); - OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ false); + OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ false, + /* should_backoff */ false); return; } else if (url_response.status_code != net::HTTP_OK) { BLOG(1, "Failed to fetch payment token"); - OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true); + OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true, + /* should_backoff */ true); return; } if (!VerifyConfirmation(confirmation)) { BLOG(1, "Failed to verify confirmation"); - OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ false); + OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ false, + /* should_backoff */ false); return; } @@ -174,7 +182,8 @@ void RedeemUnblindedToken::OnFetchPaymentToken( base::JSONReader::Read(url_response.body); if (!dictionary || !dictionary->is_dict()) { BLOG(3, "Failed to parse response: " << url_response.body); - OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true); + OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true, + /* should_backoff */ true); return; } @@ -182,7 +191,8 @@ void RedeemUnblindedToken::OnFetchPaymentToken( const std::string* id = dictionary->FindStringKey("id"); if (!id) { BLOG(0, "Response is missing id"); - OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true); + OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true, + /* should_backoff */ true); return; } @@ -190,7 +200,8 @@ void RedeemUnblindedToken::OnFetchPaymentToken( if (*id != confirmation.id) { BLOG(0, "Response id " << *id << " does not match confirmation id " << confirmation.id); - OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ false); + OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ false, + /* should_backoff */ false); return; } @@ -199,7 +210,8 @@ void RedeemUnblindedToken::OnFetchPaymentToken( dictionary->FindDictKey("paymentToken"); if (!payment_token_dictionary) { BLOG(1, "Response is missing paymentToken"); - OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true); + OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true, + /* should_backoff */ true); return; } @@ -208,7 +220,8 @@ void RedeemUnblindedToken::OnFetchPaymentToken( payment_token_dictionary->FindStringKey("publicKey"); if (!public_key_base64) { BLOG(0, "Response is missing publicKey in paymentToken dictionary"); - OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true); + OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true, + /* should_backoff */ true); return; } @@ -217,7 +230,8 @@ void RedeemUnblindedToken::OnFetchPaymentToken( if (!public_key.has_value()) { BLOG(0, "Invalid public key"); NOTREACHED(); - OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true); + OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true, + /* should_backoff */ true); return; } @@ -225,7 +239,8 @@ void RedeemUnblindedToken::OnFetchPaymentToken( *public_key_base64)) { BLOG(0, "Response public key " << *public_key_base64 << " does not exist " << "in payments issuer public keys"); - OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true); + OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true, + /* should_backoff */ true); return; } @@ -234,7 +249,8 @@ void RedeemUnblindedToken::OnFetchPaymentToken( payment_token_dictionary->FindStringKey("batchProof"); if (!batch_dleq_proof_base64) { BLOG(0, "Response is missing batchProof"); - OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true); + OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true, + /* should_backoff */ true); return; } privacy::cbr::BatchDLEQProof batch_dleq_proof = @@ -242,7 +258,8 @@ void RedeemUnblindedToken::OnFetchPaymentToken( if (!batch_dleq_proof.has_value()) { BLOG(0, "Invalid batch DLEQ proof"); NOTREACHED(); - OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true); + OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true, + /* should_backoff */ true); return; } @@ -251,13 +268,15 @@ void RedeemUnblindedToken::OnFetchPaymentToken( payment_token_dictionary->FindListKey("signedTokens"); if (!signed_tokens_list) { BLOG(0, "Response is missing signedTokens"); - OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true); + OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true, + /* should_backoff */ true); return; } if (signed_tokens_list->GetList().size() != 1) { BLOG(0, "Response has too many signedTokens"); - OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true); + OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true, + /* should_backoff */ true); return; } @@ -291,7 +310,8 @@ void RedeemUnblindedToken::OnFetchPaymentToken( BLOG(1, " Batch proof: " << *batch_dleq_proof_base64); BLOG(1, " Public key: " << *public_key_base64); - OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true); + OnFailedToRedeemUnblindedToken(confirmation, /* should_retry */ true, + /* should_backoff */ true); return; } const std::vector& @@ -339,12 +359,14 @@ void RedeemUnblindedToken::OnDidRedeemUnblindedToken( void RedeemUnblindedToken::OnFailedToRedeemUnblindedToken( const ConfirmationInfo& confirmation, - const bool should_retry) { + const bool should_retry, + const bool should_backoff) { if (!delegate_) { return; } - delegate_->OnFailedToRedeemUnblindedToken(confirmation, should_retry); + delegate_->OnFailedToRedeemUnblindedToken(confirmation, should_retry, + should_backoff); } } // namespace ads diff --git a/vendor/bat-native-ads/src/bat/ads/internal/account/utility/redeem_unblinded_token/redeem_unblinded_token.h b/vendor/bat-native-ads/src/bat/ads/internal/account/utility/redeem_unblinded_token/redeem_unblinded_token.h index ac167a2754f3..5fe940bce217 100644 --- a/vendor/bat-native-ads/src/bat/ads/internal/account/utility/redeem_unblinded_token/redeem_unblinded_token.h +++ b/vendor/bat-native-ads/src/bat/ads/internal/account/utility/redeem_unblinded_token/redeem_unblinded_token.h @@ -52,7 +52,8 @@ class RedeemUnblindedToken final { const ConfirmationInfo& confirmation, const privacy::UnblindedPaymentTokenInfo& unblinded_payment_token); void OnFailedToRedeemUnblindedToken(const ConfirmationInfo& confirmation, - const bool should_retry); + const bool should_retry, + const bool should_backoff); raw_ptr delegate_ = nullptr; }; diff --git a/vendor/bat-native-ads/src/bat/ads/internal/account/utility/redeem_unblinded_token/redeem_unblinded_token_delegate.h b/vendor/bat-native-ads/src/bat/ads/internal/account/utility/redeem_unblinded_token/redeem_unblinded_token_delegate.h index cc79537978b8..10ddf345bb88 100644 --- a/vendor/bat-native-ads/src/bat/ads/internal/account/utility/redeem_unblinded_token/redeem_unblinded_token_delegate.h +++ b/vendor/bat-native-ads/src/bat/ads/internal/account/utility/redeem_unblinded_token/redeem_unblinded_token_delegate.h @@ -32,10 +32,12 @@ class RedeemUnblindedTokenDelegate { const privacy::UnblindedPaymentTokenInfo& unblinded_payment_token) {} // Invoked to tell the delegate unblinded token redemption failed for the - // corresponding |confirmation| and whether we should retry + // corresponding |confirmation| and whether we should retry and backoff for + // subsequent failures virtual void OnFailedToRedeemUnblindedToken( const ConfirmationInfo& confirmation, - const bool should_retry) {} + const bool should_retry, + const bool should_backoff) {} }; } // namespace ads diff --git a/vendor/bat-native-ads/src/bat/ads/internal/account/utility/redeem_unblinded_token/redeem_unblinded_token_delegate_mock.h b/vendor/bat-native-ads/src/bat/ads/internal/account/utility/redeem_unblinded_token/redeem_unblinded_token_delegate_mock.h index d78d51d72bd9..9264404df2bb 100644 --- a/vendor/bat-native-ads/src/bat/ads/internal/account/utility/redeem_unblinded_token/redeem_unblinded_token_delegate_mock.h +++ b/vendor/bat-native-ads/src/bat/ads/internal/account/utility/redeem_unblinded_token/redeem_unblinded_token_delegate_mock.h @@ -37,7 +37,9 @@ class RedeemUnblindedTokenDelegateMock : public RedeemUnblindedTokenDelegate { MOCK_METHOD(void, OnFailedToRedeemUnblindedToken, - (const ConfirmationInfo& confirmation, const bool should_retry)); + (const ConfirmationInfo& confirmation, + const bool should_retry, + const bool should_backoff)); }; } // namespace ads diff --git a/vendor/bat-native-ads/src/bat/ads/internal/account/utility/redeem_unblinded_token/redeem_unblinded_token_unittest.cc b/vendor/bat-native-ads/src/bat/ads/internal/account/utility/redeem_unblinded_token/redeem_unblinded_token_unittest.cc index cfa38180ddff..7ccf54b67149 100644 --- a/vendor/bat-native-ads/src/bat/ads/internal/account/utility/redeem_unblinded_token/redeem_unblinded_token_unittest.cc +++ b/vendor/bat-native-ads/src/bat/ads/internal/account/utility/redeem_unblinded_token/redeem_unblinded_token_unittest.cc @@ -104,7 +104,7 @@ TEST_F(BatAdsRedeemUnblindedTokenTest, RedeemUnblindedTokenIfAdsAreEnabled) { .Times(1); EXPECT_CALL(*redeem_unblinded_token_delegate_mock_, - OnFailedToRedeemUnblindedToken(_, _)) + OnFailedToRedeemUnblindedToken(_, _, _)) .Times(0); redeem_unblinded_token_->Redeem(confirmation); @@ -140,7 +140,9 @@ TEST_F(BatAdsRedeemUnblindedTokenTest, .Times(0); EXPECT_CALL(*redeem_unblinded_token_delegate_mock_, - OnFailedToRedeemUnblindedToken(expected_confirmation, true)) + OnFailedToRedeemUnblindedToken(expected_confirmation, + /* should_retry */ true, + /* should_backoff */ true)) .Times(1); redeem_unblinded_token_->Redeem(confirmation); @@ -200,7 +202,7 @@ TEST_F(BatAdsRedeemUnblindedTokenTest, .Times(1); EXPECT_CALL(*redeem_unblinded_token_delegate_mock_, - OnFailedToRedeemUnblindedToken(_, _)) + OnFailedToRedeemUnblindedToken(_, _, _)) .Times(0); redeem_unblinded_token_->Redeem(confirmation); @@ -217,7 +219,7 @@ TEST_F( const URLEndpoints& endpoints = { {// Create confirmation request R"(/v2/confirmation/d990ed8d-d739-49fb-811b-c2e02158fb60/eyJwYXlsb2FkIjoie1wiYmxpbmRlZFBheW1lbnRUb2tlblwiOlwiRXY1SkU0LzlUWkkvNVRxeU45SldmSjFUbzBIQndRdzJyV2VBUGNkalgzUT1cIixcImJ1aWxkQ2hhbm5lbFwiOlwidGVzdFwiLFwiY3JlYXRpdmVJbnN0YW5jZUlkXCI6XCI3MDgyOWQ3MS1jZTJlLTQ0ODMtYTRjMC1lMWUyYmVlOTY1MjBcIixcInBheWxvYWRcIjp7fSxcInBsYXRmb3JtXCI6XCJ0ZXN0XCIsXCJ0eXBlXCI6XCJ2aWV3XCJ9Iiwic2lnbmF0dXJlIjoiRkhiczQxY1h5eUF2SnkxUE9HVURyR1FoeUtjRkVMSXVJNU5yT3NzT2VLbUV6N1p5azZ5aDhweDQ0WmFpQjZFZkVRc0pWMEpQYmJmWjVUMGt2QmhEM0E9PSIsInQiOiJWV0tFZEliOG5Nd21UMWVMdE5MR3VmVmU2TlFCRS9TWGpCcHlsTFlUVk1KVFQrZk5ISTJWQmQyenRZcUlwRVdsZWF6TiswYk5jNGF2S2ZrY3YyRkw3Zz09In0=)", - {{net::HTTP_BAD_REQUEST, ""}}}, + {{net::HTTP_OK, ""}}}, {// Fetch payment token request R"(/v2/confirmation/d990ed8d-d739-49fb-811b-c2e02158fb60/paymentToken)", {{net::HTTP_NOT_FOUND, ""}}}}; @@ -249,7 +251,60 @@ TEST_F( .Times(0); EXPECT_CALL(*redeem_unblinded_token_delegate_mock_, - OnFailedToRedeemUnblindedToken(expected_confirmation, true)) + OnFailedToRedeemUnblindedToken(expected_confirmation, + /* should_retry */ true, + /* should_backoff */ false)) + .Times(1); + + redeem_unblinded_token_->Redeem(confirmation); + + // Assert +} + +TEST_F( + BatAdsRedeemUnblindedTokenTest, + FailAndRetryToRedeemUnblindedTokenDueToFetchPaymentTokenRespondingWithAcceptedIfAdsAreEnabled) { // NOLINT + // Arrange + AdsClientHelper::Get()->SetBooleanPref(prefs::kEnabled, true); + + const URLEndpoints& endpoints = { + {// Create confirmation request + R"(/v2/confirmation/d990ed8d-d739-49fb-811b-c2e02158fb60/eyJwYXlsb2FkIjoie1wiYmxpbmRlZFBheW1lbnRUb2tlblwiOlwiRXY1SkU0LzlUWkkvNVRxeU45SldmSjFUbzBIQndRdzJyV2VBUGNkalgzUT1cIixcImJ1aWxkQ2hhbm5lbFwiOlwidGVzdFwiLFwiY3JlYXRpdmVJbnN0YW5jZUlkXCI6XCI3MDgyOWQ3MS1jZTJlLTQ0ODMtYTRjMC1lMWUyYmVlOTY1MjBcIixcInBheWxvYWRcIjp7fSxcInBsYXRmb3JtXCI6XCJ0ZXN0XCIsXCJ0eXBlXCI6XCJ2aWV3XCJ9Iiwic2lnbmF0dXJlIjoiRkhiczQxY1h5eUF2SnkxUE9HVURyR1FoeUtjRkVMSXVJNU5yT3NzT2VLbUV6N1p5azZ5aDhweDQ0WmFpQjZFZkVRc0pWMEpQYmJmWjVUMGt2QmhEM0E9PSIsInQiOiJWV0tFZEliOG5Nd21UMWVMdE5MR3VmVmU2TlFCRS9TWGpCcHlsTFlUVk1KVFQrZk5ISTJWQmQyenRZcUlwRVdsZWF6TiswYk5jNGF2S2ZrY3YyRkw3Zz09In0=)", + {{net::HTTP_OK, ""}}}, + {// Fetch payment token request + R"(/v2/confirmation/d990ed8d-d739-49fb-811b-c2e02158fb60/paymentToken)", + {{net::HTTP_ACCEPTED, ""}}}}; + MockUrlRequest(ads_client_mock_, endpoints); + + BuildAndSetIssuers(); + + privacy::SetUnblindedTokens(1); + + const ConfirmationInfo& confirmation = + BuildConfirmation("d990ed8d-d739-49fb-811b-c2e02158fb60", + "8b742869-6e4a-490c-ac31-31b49130098a", + "546fe7b0-5047-4f28-a11c-81f14edcf0f6", + ConfirmationType::kViewed, AdType::kNotificationAd); + + // Act + ConfirmationInfo expected_confirmation = confirmation; + expected_confirmation.was_created = true; + + EXPECT_CALL(*redeem_unblinded_token_delegate_mock_, OnDidSendConfirmation(_)) + .Times(0); + + EXPECT_CALL(*redeem_unblinded_token_delegate_mock_, + OnFailedToSendConfirmation(_, _)) + .Times(0); + + EXPECT_CALL(*redeem_unblinded_token_delegate_mock_, + OnDidRedeemUnblindedToken(_, _)) + .Times(0); + + EXPECT_CALL(*redeem_unblinded_token_delegate_mock_, + OnFailedToRedeemUnblindedToken(expected_confirmation, + /* should_retry */ true, + /* should_backoff */ false)) .Times(1); redeem_unblinded_token_->Redeem(confirmation); @@ -298,7 +353,9 @@ TEST_F( .Times(0); EXPECT_CALL(*redeem_unblinded_token_delegate_mock_, - OnFailedToRedeemUnblindedToken(expected_confirmation, true)) + OnFailedToRedeemUnblindedToken(expected_confirmation, + /* should_retry */ true, + /* should_backoff */ true)) .Times(1); redeem_unblinded_token_->Redeem(confirmation); @@ -347,7 +404,7 @@ TEST_F(BatAdsRedeemUnblindedTokenTest, SendConfirmationIfAdsIsDisabled) { .Times(0); EXPECT_CALL(*redeem_unblinded_token_delegate_mock_, - OnFailedToRedeemUnblindedToken(_, _)) + OnFailedToRedeemUnblindedToken(_, _, _)) .Times(0); redeem_unblinded_token_->Redeem(confirmation); @@ -387,7 +444,7 @@ TEST_F(BatAdsRedeemUnblindedTokenTest, .Times(0); EXPECT_CALL(*redeem_unblinded_token_delegate_mock_, - OnFailedToRedeemUnblindedToken(_, _)) + OnFailedToRedeemUnblindedToken(_, _, _)) .Times(0); redeem_unblinded_token_->Redeem(confirmation); @@ -427,7 +484,7 @@ TEST_F(BatAdsRedeemUnblindedTokenTest, .Times(0); EXPECT_CALL(*redeem_unblinded_token_delegate_mock_, - OnFailedToRedeemUnblindedToken(_, _)) + OnFailedToRedeemUnblindedToken(_, _, _)) .Times(0); redeem_unblinded_token_->Redeem(confirmation); @@ -467,7 +524,7 @@ TEST_F(BatAdsRedeemUnblindedTokenTest, .Times(0); EXPECT_CALL(*redeem_unblinded_token_delegate_mock_, - OnFailedToRedeemUnblindedToken(_, _)) + OnFailedToRedeemUnblindedToken(_, _, _)) .Times(0); redeem_unblinded_token_->Redeem(confirmation);