Skip to content

Commit 3ee0296

Browse files
authored
use the lowest denominator budget coin during recovery (#2988)
1 parent 18ab86e commit 3ee0296

File tree

3 files changed

+127
-71
lines changed

3 files changed

+127
-71
lines changed

utt/privacy-wallet-service/src/PrivacyService.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ void PrivacyWalletService::Shutdown() {
5454
PrivacyWalletServiceImpl::PrivacyWalletServiceImpl() {
5555
try {
5656
wallet_ = Wallet::recoverFromStorage(PrivacyWalletServiceImpl::wallet_db_path);
57+
} catch (std::exception& e) {
58+
std::cout << "Caught exception: " << e.what() << std::endl;
59+
std::exit(0);
5760
} catch (...) {
5861
std::cout << "storage is corrupted, unable to restore the wallet service from the given storage" << std::endl;
5962
std::exit(0);

utt/privacy-wallet-service/tests/grpc-service/grpc-server-test.cpp

+107-68
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,78 @@ class test_privacy_wallet_grpc_service : public libutt::api::testing::test_utt_i
203203
ASSERT_TRUE(status.ok());
204204
}
205205
}
206+
207+
void singleTransferTest() {
208+
configureWallet(0);
209+
runFullRegistrationCycle();
210+
createBudgetCoin();
211+
mintRegularCoin(1000);
212+
213+
PrivacyWalletRequest request;
214+
auto transfer_req = request.mutable_generate_transfer_tx_request();
215+
transfer_req->set_amount(100);
216+
transfer_req->set_recipient_pid("user-2");
217+
transfer_req->set_recipient_public_key({pkeys[1].begin(), pkeys[1].end()});
218+
auto context = grpc::ClientContext{};
219+
auto response = PrivacyWalletResponse{};
220+
grpc::Status status = stub_->PrivacyWalletService(&context, request, &response);
221+
ASSERT_TRUE(status.ok());
222+
ASSERT_TRUE(response.has_generate_tx_response());
223+
auto& tx_data = response.generate_tx_response();
224+
ASSERT_TRUE(tx_data.final());
225+
ASSERT_EQ(tx_data.num_of_output_coins(), 3);
226+
auto sigs = signTx<libutt::api::operations::Transaction>({tx_data.tx().begin(), tx_data.tx().end()});
227+
228+
auto context2 = grpc::ClientContext{};
229+
PrivacyWalletRequest request2;
230+
auto response2 = PrivacyWalletResponse{};
231+
auto claim_coins_req = request2.mutable_claim_coins_request();
232+
claim_coins_req->set_tx(tx_data.tx());
233+
for (const auto& sig : sigs) claim_coins_req->add_sigs({sig.begin(), sig.end()});
234+
claim_coins_req->set_type(TxType::TRANSFER);
235+
auto status2 = stub_->PrivacyWalletService(&context2, request2, &response2);
236+
ASSERT_TRUE(status2.ok());
237+
}
238+
239+
void mergeTransferCyclesTest() {
240+
configureWallet(0);
241+
runFullRegistrationCycle();
242+
createBudgetCoin();
243+
mintRegularCoin(1);
244+
mintRegularCoin(1);
245+
mintRegularCoin(1);
246+
mintRegularCoin(1);
247+
uint64_t cycles = 0;
248+
bool is_final = false;
249+
while (!is_final) {
250+
cycles++;
251+
PrivacyWalletRequest request;
252+
auto transfer_req = request.mutable_generate_transfer_tx_request();
253+
transfer_req->set_amount(4);
254+
transfer_req->set_recipient_pid("user-2");
255+
transfer_req->set_recipient_public_key({pkeys[1].begin(), pkeys[1].end()});
256+
auto context = grpc::ClientContext{};
257+
auto response = PrivacyWalletResponse{};
258+
grpc::Status status = stub_->PrivacyWalletService(&context, request, &response);
259+
ASSERT_TRUE(status.ok());
260+
ASSERT_TRUE(response.has_generate_tx_response());
261+
auto& tx_data = response.generate_tx_response();
262+
is_final = tx_data.final();
263+
auto sigs = signTx<libutt::api::operations::Transaction>({tx_data.tx().begin(), tx_data.tx().end()});
264+
ASSERT_EQ(tx_data.num_of_output_coins(), is_final ? 2 : 1);
265+
auto context2 = grpc::ClientContext{};
266+
PrivacyWalletRequest request2;
267+
auto response2 = PrivacyWalletResponse{};
268+
auto claim_coins_req = request2.mutable_claim_coins_request();
269+
claim_coins_req->set_tx(tx_data.tx());
270+
for (const auto& sig : sigs) claim_coins_req->add_sigs({sig.begin(), sig.end()});
271+
claim_coins_req->set_type(TxType::TRANSFER);
272+
auto status2 = stub_->PrivacyWalletService(&context2, request2, &response2);
273+
ASSERT_TRUE(status2.ok());
274+
}
275+
ASSERT_EQ(cycles, 3);
276+
}
277+
206278
void restartServer() {
207279
server_->Shutdown();
208280
(void)server_.release();
@@ -410,85 +482,52 @@ TEST_F(test_privacy_wallet_grpc_service, test_break_and_burn_cycles) {
410482
ASSERT_EQ(cycles, 2);
411483
}
412484

413-
TEST_F(test_privacy_wallet_grpc_service, test_transfer_single_cycle) {
414-
configureWallet(0);
415-
runFullRegistrationCycle();
416-
createBudgetCoin();
417-
mintRegularCoin(1000);
485+
TEST_F(test_privacy_wallet_grpc_service, test_transfer_single_cycle) { singleTransferTest(); }
486+
487+
TEST_F(test_privacy_wallet_grpc_service, test_merge_and_transfer_cycles) { mergeTransferCyclesTest(); }
488+
489+
TEST_F(test_privacy_wallet_grpc_service, test_server_restore_single_transfer) {
490+
singleTransferTest();
491+
492+
restartServer();
493+
494+
auto [rstatus, _] = configureWallet(0);
495+
ASSERT_EQ(rstatus.error_code(), grpc::StatusCode::ALREADY_EXISTS);
496+
ASSERT_EQ(rstatus.error_message(), "wallet is already configured");
418497

419498
PrivacyWalletRequest request;
420-
auto transfer_req = request.mutable_generate_transfer_tx_request();
421-
transfer_req->set_amount(100);
422-
transfer_req->set_recipient_pid("user-2");
423-
transfer_req->set_recipient_public_key({pkeys[1].begin(), pkeys[1].end()});
499+
request.mutable_get_state_request();
424500
auto context = grpc::ClientContext{};
425501
auto response = PrivacyWalletResponse{};
426502
grpc::Status status = stub_->PrivacyWalletService(&context, request, &response);
427503
ASSERT_TRUE(status.ok());
428-
ASSERT_TRUE(response.has_generate_tx_response());
429-
auto& tx_data = response.generate_tx_response();
430-
ASSERT_TRUE(tx_data.final());
431-
ASSERT_EQ(tx_data.num_of_output_coins(), 3);
432-
auto sigs = signTx<libutt::api::operations::Transaction>({tx_data.tx().begin(), tx_data.tx().end()});
433-
434-
auto context2 = grpc::ClientContext{};
435-
PrivacyWalletRequest request2;
436-
auto response2 = PrivacyWalletResponse{};
437-
auto claim_coins_req = request2.mutable_claim_coins_request();
438-
claim_coins_req->set_tx(tx_data.tx());
439-
for (const auto& sig : sigs) claim_coins_req->add_sigs({sig.begin(), sig.end()});
440-
claim_coins_req->set_type(TxType::TRANSFER);
441-
auto status2 = stub_->PrivacyWalletService(&context2, request2, &response2);
442-
ASSERT_TRUE(status2.ok());
504+
ASSERT_TRUE(response.has_get_state_response());
505+
auto& resp = response.get_state_response();
506+
std::cout << "Budget: " << resp.budget() << std::endl;
507+
// single transfer removed 100 budget.
508+
ASSERT_EQ(resp.budget(), 900);
443509
}
444510

445-
TEST_F(test_privacy_wallet_grpc_service, test_merge_and_transfer_cycles) {
446-
configureWallet(0);
447-
runFullRegistrationCycle();
448-
createBudgetCoin();
449-
mintRegularCoin(1);
450-
mintRegularCoin(1);
451-
mintRegularCoin(1);
452-
mintRegularCoin(1);
453-
uint64_t cycles = 0;
454-
bool is_final = false;
455-
while (!is_final) {
456-
cycles++;
457-
PrivacyWalletRequest request;
458-
auto transfer_req = request.mutable_generate_transfer_tx_request();
459-
transfer_req->set_amount(4);
460-
transfer_req->set_recipient_pid("user-2");
461-
transfer_req->set_recipient_public_key({pkeys[1].begin(), pkeys[1].end()});
462-
auto context = grpc::ClientContext{};
463-
auto response = PrivacyWalletResponse{};
464-
grpc::Status status = stub_->PrivacyWalletService(&context, request, &response);
465-
ASSERT_TRUE(status.ok());
466-
ASSERT_TRUE(response.has_generate_tx_response());
467-
auto& tx_data = response.generate_tx_response();
468-
is_final = tx_data.final();
469-
auto sigs = signTx<libutt::api::operations::Transaction>({tx_data.tx().begin(), tx_data.tx().end()});
470-
ASSERT_EQ(tx_data.num_of_output_coins(), is_final ? 2 : 1);
471-
auto context2 = grpc::ClientContext{};
472-
PrivacyWalletRequest request2;
473-
auto response2 = PrivacyWalletResponse{};
474-
auto claim_coins_req = request2.mutable_claim_coins_request();
475-
claim_coins_req->set_tx(tx_data.tx());
476-
for (const auto& sig : sigs) claim_coins_req->add_sigs({sig.begin(), sig.end()});
477-
claim_coins_req->set_type(TxType::TRANSFER);
478-
auto status2 = stub_->PrivacyWalletService(&context2, request2, &response2);
479-
ASSERT_TRUE(status2.ok());
480-
}
481-
ASSERT_EQ(cycles, 3);
482-
}
511+
TEST_F(test_privacy_wallet_grpc_service, test_server_restore_multiple_transfer) {
512+
mergeTransferCyclesTest();
483513

484-
TEST_F(test_privacy_wallet_grpc_service, test_server_state_restore) {
485-
configureWallet(0);
486514
restartServer();
487515

488-
auto [status2, _] = configureWallet(0);
489-
ASSERT_EQ(status2.error_code(), grpc::StatusCode::ALREADY_EXISTS);
490-
ASSERT_EQ(status2.error_message(), "wallet is already configured");
491-
(void)_;
516+
auto [rstatus, _] = configureWallet(0);
517+
ASSERT_EQ(rstatus.error_code(), grpc::StatusCode::ALREADY_EXISTS);
518+
ASSERT_EQ(rstatus.error_message(), "wallet is already configured");
519+
520+
PrivacyWalletRequest request;
521+
request.mutable_get_state_request();
522+
auto context = grpc::ClientContext{};
523+
auto response = PrivacyWalletResponse{};
524+
grpc::Status status = stub_->PrivacyWalletService(&context, request, &response);
525+
ASSERT_TRUE(status.ok());
526+
ASSERT_TRUE(response.has_get_state_response());
527+
auto& resp = response.get_state_response();
528+
std::cout << "Budget: " << resp.budget() << std::endl;
529+
// account for 4 transfers
530+
ASSERT_EQ(resp.budget(), 996);
492531
}
493532

494533
TEST_F(test_privacy_wallet_grpc_service, test_set_and_get_application_data) {

utt/utt-client-api/src/User.cpp

+17-3
Original file line numberDiff line numberDiff line change
@@ -316,11 +316,25 @@ std::unique_ptr<User> User::recoverFromStorage(std::shared_ptr<IStorage> storage
316316
if (c.getType() == libutt::api::Coin::Normal) {
317317
user->pImpl_->coins_.emplace(c.getNullifier(), c);
318318
} else if (c.getType() == libutt::api::Coin::Budget) {
319+
// Currently the original budget coin and change budget coins would
320+
// be stored in assets.
321+
// Until budget coin pool is supported, use the budget with lowest value
322+
// to stick to current single outstanding budget workflow model
319323
if (user->pImpl_->budgetNullifiers_.size() >= 1) {
320-
throw std::runtime_error("Currently multiple budget coins are not supported");
324+
if (c.getVal() < user->pImpl_->budgetCoin_.value().getVal()) {
325+
user->pImpl_->budgetCoin_.emplace(c);
326+
//@todo assuming nullifier list is not used except for book keeping
327+
user->pImpl_->budgetNullifiers_.clear();
328+
user->pImpl_->budgetNullifiers_.insert(c.getNullifier());
329+
std::cout << "warn: mutiple budget coins found updating with least value.." << c.getVal() << std::endl;
330+
} else {
331+
std::cout << "warn: ignore budget coins value: " << c.getVal()
332+
<< " use lower value: " << user->pImpl_->budgetCoin_.value().getVal() << std::endl;
333+
}
334+
} else {
335+
user->pImpl_->budgetCoin_.emplace(c);
336+
user->pImpl_->budgetNullifiers_.insert(c.getNullifier());
321337
}
322-
user->pImpl_->budgetCoin_.emplace(c);
323-
user->pImpl_->budgetNullifiers_.insert(c.getNullifier());
324338
}
325339
}
326340
return user;

0 commit comments

Comments
 (0)