Skip to content

Commit 8e1e193

Browse files
author
Yehonatan Buchnik
authored
Recover the wallet service implicitly on startup (#2968)
Currently, to recover the wallet service from storage, the user has to call explicitly to the configure rpc command. This PR fixes it, such that once there is a backup, the wallet service will restore itself.
1 parent b661610 commit 8e1e193

File tree

16 files changed

+221
-44
lines changed

16 files changed

+221
-44
lines changed

utt/include/config.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ class PublicConfig {
3737
PublicConfig(PublicConfig&& o);
3838
PublicConfig& operator=(PublicConfig&& o);
3939

40-
bool operator==(const PublicConfig& o);
41-
bool operator!=(const PublicConfig& o);
40+
bool operator==(const PublicConfig& o) const;
41+
bool operator!=(const PublicConfig& o) const;
4242

4343
std::string getCommitVerificationKey() const;
4444
std::string getRegistrationVerificationKey() const;

utt/libutt/src/api/config.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ PublicConfig::~PublicConfig() = default;
3434
PublicConfig::PublicConfig(PublicConfig&& o) = default;
3535
PublicConfig& PublicConfig::operator=(PublicConfig&& o) = default;
3636

37-
bool PublicConfig::operator==(const PublicConfig& o) {
37+
bool PublicConfig::operator==(const PublicConfig& o) const {
3838
return pImpl_->params_ == o.pImpl_->params_ && pImpl_->commitVerificationKey_ == o.pImpl_->commitVerificationKey_ &&
3939
pImpl_->registrationVerificationKey_ == o.pImpl_->registrationVerificationKey_;
4040
}
4141

42-
bool PublicConfig::operator!=(const PublicConfig& o) { return !operator==(o); }
42+
bool PublicConfig::operator!=(const PublicConfig& o) const { return !operator==(o); }
4343

4444
std::string PublicConfig::getCommitVerificationKey() const {
4545
return libutt::serialize<libutt::RandSigPK>(pImpl_->commitVerificationKey_);

utt/privacy-wallet-lib/include/storage/FileBasedUserStorage.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,16 @@ class FileBasedUserStorage : public IStorage {
3232
void removeCoin(const libutt::api::Coin&) override;
3333
void startTransaction() override;
3434
void commit() override;
35+
void setUserId(const std::string& user_id) override;
36+
void setUttPublicConfig(const libutt::api::PublicConfig& utt_public_config) override;
3537

3638
libutt::api::types::CurvePoint getClientSideSecret() override;
3739
libutt::api::types::CurvePoint getSystemSideSecret() override;
3840
libutt::api::types::Signature getRcmSignature() override;
3941
std::vector<libutt::api::Coin> getCoins() override;
4042
std::pair<std::string, std::string> getKeyPair() override;
43+
std::string getUserId() override;
44+
libutt::api::PublicConfig getUttPublicConfig() override;
4145

4246
protected:
4347
std::string state_path_;

utt/privacy-wallet-lib/src/storage/FileBasedUserStorage.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,20 @@ void FileBasedUserStorage::setCoin(const libutt::api::Coin& c) {
117117
state_["coins"][bytesToHex(c.getNullifier())] = bytesToHex(libutt::api::serialize(c));
118118
}
119119

120+
void FileBasedUserStorage::setUserId(const std::string& user_id) {
121+
if (state_.contains("user_id") && getUserId() != user_id) {
122+
throw std::runtime_error("user id is already set");
123+
}
124+
state_["user_id"] = user_id;
125+
}
126+
127+
void FileBasedUserStorage::setUttPublicConfig(const libutt::api::PublicConfig& utt_public_config) {
128+
if (state_.contains("utt_public_config") && getUttPublicConfig() != utt_public_config) {
129+
throw std::runtime_error("utt public configuration is already set");
130+
}
131+
state_["utt_public_config"] = bytesToHex(libutt::api::serialize(utt_public_config));
132+
}
133+
120134
void FileBasedUserStorage::removeCoin(const libutt::api::Coin& c) {
121135
state_["coins"].erase(bytesToHex(c.getNullifier()));
122136
}
@@ -160,4 +174,15 @@ std::pair<std::string, std::string> FileBasedUserStorage::getKeyPair() {
160174
auto pk = hexStringToBytes(state_["key_pair"]["pk"]);
161175
return {std::string(sk.begin(), sk.end()), std::string(pk.begin(), pk.end())};
162176
}
177+
178+
std::string FileBasedUserStorage::getUserId() {
179+
if (!state_.contains("user_id")) return std::string();
180+
return state_["user_id"];
181+
}
182+
183+
libutt::api::PublicConfig FileBasedUserStorage::getUttPublicConfig() {
184+
if (!state_.contains("utt_public_config")) return libutt::api::PublicConfig();
185+
return libutt::api::deserialize<libutt::api::PublicConfig>(hexStringToBytes(state_["utt_public_config"]));
186+
}
187+
163188
} // namespace utt::client

utt/privacy-wallet-lib/tests/storage/FileBasedStorageTests.cpp

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class test_utt_storage : public libutt::api::testing::test_utt_instance {
4040
void restartStorage() { storage_ = std::make_unique<utt::client::FileBasedUserStorage>(storage_path); }
4141

4242
std::string storage_path = "./test_storage";
43-
std::unique_ptr<utt::client::IStorage> storage_;
43+
std::unique_ptr<utt::client::FileBasedUserStorage> storage_;
4444
};
4545
TEST_F(test_utt_storage, test_isNewStorage) {
4646
ASSERT_TRUE(storage_->isNewStorage());
@@ -54,6 +54,60 @@ TEST_F(test_utt_storage, test_isNewStorage_negative) {
5454
ASSERT_TRUE(storage_->isNewStorage());
5555
}
5656

57+
TEST_F(test_utt_storage, test_set_user_id) {
58+
ASSERT_TRUE(storage_->isNewStorage());
59+
{
60+
IStorage::tx_guard g(*storage_);
61+
storage_->setUserId("user_1");
62+
}
63+
restartStorage();
64+
ASSERT_FALSE(storage_->isNewStorage());
65+
ASSERT_EQ(storage_->getUserId(), "user_1");
66+
}
67+
68+
TEST_F(test_utt_storage, test_get_empty_user_id) {
69+
ASSERT_TRUE(storage_->isNewStorage());
70+
ASSERT_EQ(storage_->getUserId(), "");
71+
}
72+
73+
TEST_F(test_utt_storage, test_assert_on_resetting_client_id) {
74+
ASSERT_TRUE(storage_->isNewStorage());
75+
{
76+
IStorage::tx_guard g(*storage_);
77+
storage_->setUserId("user_1");
78+
}
79+
restartStorage();
80+
ASSERT_NO_THROW(storage_->setUserId("user_1"));
81+
ASSERT_ANY_THROW(storage_->setUserId("user_2"));
82+
}
83+
84+
TEST_F(test_utt_storage, test_set_utt_public_config) {
85+
ASSERT_TRUE(storage_->isNewStorage());
86+
{
87+
IStorage::tx_guard g(*storage_);
88+
storage_->setUttPublicConfig(config->getPublicConfig());
89+
}
90+
restartStorage();
91+
ASSERT_FALSE(storage_->isNewStorage());
92+
ASSERT_EQ(storage_->getUttPublicConfig(), config->getPublicConfig());
93+
}
94+
95+
TEST_F(test_utt_storage, test_get_empty_utt_public_config) {
96+
ASSERT_TRUE(storage_->isNewStorage());
97+
ASSERT_EQ(storage_->getUttPublicConfig(), libutt::api::PublicConfig());
98+
}
99+
100+
TEST_F(test_utt_storage, test_assert_on_utt_public_config) {
101+
ASSERT_TRUE(storage_->isNewStorage());
102+
{
103+
IStorage::tx_guard g(*storage_);
104+
storage_->setUttPublicConfig(config->getPublicConfig());
105+
}
106+
restartStorage();
107+
ASSERT_NO_THROW(storage_->setUttPublicConfig(config->getPublicConfig()));
108+
ASSERT_ANY_THROW(storage_->setUttPublicConfig(libutt::api::PublicConfig()));
109+
}
110+
57111
TEST_F(test_utt_storage, test_setKeyPair) {
58112
std::pair<std::string, std::string> keypair = {pr_keys.front(), pkeys.front()};
59113
{

utt/privacy-wallet-service/include/PrivacyService.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ namespace utt::walletservice {
2626
//@TODO hide on its own file..
2727
class PrivacyWalletServiceImpl final : public vmware::concord::privacy::wallet::api::v1::PrivacyWalletService::Service {
2828
public:
29-
PrivacyWalletServiceImpl() {}
29+
PrivacyWalletServiceImpl();
3030
::grpc::Status PrivacyWalletService(
3131
::grpc::ServerContext* context,
3232
const ::vmware::concord::privacy::wallet::api::v1::PrivacyWalletRequest* request,

utt/privacy-wallet-service/include/Wallet.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,10 @@ class Wallet {
4747
uint64_t getBudget() const;
4848
bool isRegistered() const;
4949
std::vector<utt::client::CoinDescriptor> getCoinsDescriptors() const;
50+
static std::unique_ptr<Wallet> recoverFromStorage(const std::string& storage_path);
5051

5152
private:
53+
Wallet() = default;
5254
std::string userId_;
5355
std::string private_key_;
5456
std::unique_ptr<utt::client::User> user_;

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

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@
2020
using namespace utt::client::utils::crypto;
2121
using namespace ::vmware::concord::privacy::wallet::api::v1;
2222
namespace utt::walletservice {
23-
PrivacyWalletService::PrivacyWalletService() : privacy_wallet_service_(std::make_unique<PrivacyWalletServiceImpl>()) {
23+
const std::string PrivacyWalletServiceImpl::wallet_db_path = "wallet-db";
24+
25+
PrivacyWalletService::PrivacyWalletService() {
2426
utt::client::Initialize();
27+
privacy_wallet_service_ = std::make_unique<PrivacyWalletServiceImpl>();
2528
}
2629

2730
PrivacyWalletService::~PrivacyWalletService() { std::cout << " Destroying privacy wallet service...\n"; }
@@ -48,9 +51,18 @@ void PrivacyWalletService::Shutdown() {
4851
std::cout << "server shutdown complete.." << std::endl;
4952
}
5053
}
54+
PrivacyWalletServiceImpl::PrivacyWalletServiceImpl() {
55+
try {
56+
wallet_ = Wallet::recoverFromStorage(PrivacyWalletServiceImpl::wallet_db_path);
57+
} catch (...) {
58+
std::cout << "storage is corrupted, unable to restore the wallet service from the given storage" << std::endl;
59+
std::exit(0);
60+
}
5161

52-
const std::string PrivacyWalletServiceImpl::wallet_db_path = "wallet-db";
53-
62+
if (wallet_) {
63+
std::cout << "wallet service recovered from storage" << std::endl;
64+
}
65+
}
5466
::grpc::Status PrivacyWalletServiceImpl::PrivacyWalletService(::grpc::ServerContext* context,
5567
const PrivacyWalletRequest* request,
5668
PrivacyWalletResponse* response) {

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,18 @@ Wallet::Wallet(std::string userId,
3030
if (!user_) throw std::runtime_error("Failed to create user!");
3131
registered_ = user_->hasRegistrationCommitment();
3232
}
33+
34+
std::unique_ptr<Wallet> Wallet::recoverFromStorage(const std::string& storage_path) {
35+
auto storage_ = std::make_shared<utt::client::FileBasedUserStorage>(storage_path);
36+
if (storage_->isNewStorage()) return nullptr;
37+
std::unique_ptr<Wallet> wallet = std::unique_ptr<Wallet>(new Wallet());
38+
wallet->userId_ = storage_->getUserId();
39+
wallet->private_key_ = storage_->getKeyPair().first;
40+
wallet->user_ = utt::client::loadUserFromStorage(storage_);
41+
wallet->registered_ = wallet->user_->hasRegistrationCommitment();
42+
return wallet;
43+
}
44+
3345
std::optional<Wallet::RegistrationInput> Wallet::generateRegistrationInput() {
3446
if (registered_) return std::nullopt;
3547
Wallet::RegistrationInput ret;

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

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,13 @@ using namespace ::vmware::concord::privacy::wallet::api::v1;
4747
class test_privacy_wallet_grpc_service : public libutt::api::testing::test_utt_instance {
4848
protected:
4949
void SetUp() override {
50+
server_ = std::make_unique<utt::walletservice::PrivacyWalletService>();
5051
libutt::api::testing::test_utt_instance::setUp(false, false, false);
51-
server_.StartServer(grpc_uri_);
52+
server_->StartServer(grpc_uri_);
5253
}
5354
void TearDown() override {
5455
fs::remove_all("wallet-db");
55-
server_.Shutdown();
56+
server_->Shutdown();
5657
}
5758

5859
std::pair<grpc::Status, PrivacyWalletResponse> configureWallet(size_t index) {
@@ -202,8 +203,18 @@ class test_privacy_wallet_grpc_service : public libutt::api::testing::test_utt_i
202203
ASSERT_TRUE(status.ok());
203204
}
204205
}
206+
void restartServer() {
207+
server_->Shutdown();
208+
(void)server_.release();
209+
server_ = std::make_unique<utt::walletservice::PrivacyWalletService>();
210+
// We listen on a different port to avoid Error code 14 : failed to connect to all addresses
211+
std::string grpc_uri_2 = "127.0.0.1:50052";
212+
server_->StartServer(grpc_uri_2);
213+
channel_ = grpc::CreateChannel(grpc_uri_2, grpc::InsecureChannelCredentials());
214+
stub_ = ::vmware::concord::privacy::wallet::api::v1::PrivacyWalletService::NewStub(channel_);
215+
}
205216
const std::string grpc_uri_{"127.0.0.1:50051"};
206-
utt::walletservice::PrivacyWalletService server_;
217+
std::unique_ptr<utt::walletservice::PrivacyWalletService> server_;
207218
std::shared_ptr<grpc::Channel> channel_ = grpc::CreateChannel(grpc_uri_, grpc::InsecureChannelCredentials());
208219
std::unique_ptr<::vmware::concord::privacy::wallet::api::v1::PrivacyWalletService::Stub> stub_ =
209220
::vmware::concord::privacy::wallet::api::v1::PrivacyWalletService::NewStub(channel_);
@@ -470,6 +481,15 @@ TEST_F(test_privacy_wallet_grpc_service, test_merge_and_transfer_cycles) {
470481
ASSERT_EQ(cycles, 3);
471482
}
472483

484+
TEST_F(test_privacy_wallet_grpc_service, test_server_state_restore) {
485+
configureWallet(0);
486+
restartServer();
487+
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)_;
492+
}
473493
} // namespace
474494

475495
int main(int argc, char** argv) {

0 commit comments

Comments
 (0)