Skip to content

Commit bebef64

Browse files
j-bermannahuhh
authored andcommitted
wallet: improve lookahead logic & make rpc persistent
1 parent 1c29aa3 commit bebef64

File tree

5 files changed

+147
-31
lines changed

5 files changed

+147
-31
lines changed

src/wallet/wallet2.cpp

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1965,26 +1965,41 @@ void wallet2::set_subaddress_lookahead(size_t major, size_t minor)
19651965
THROW_WALLET_EXCEPTION_IF(minor == 0, error::wallet_internal_error, "Subaddress minor lookahead may not be zero");
19661966
THROW_WALLET_EXCEPTION_IF(minor > 0xffffffff, error::wallet_internal_error, "Subaddress minor lookahead is too large");
19671967

1968-
if (major > m_subaddress_lookahead_major) { // if increasing the lookahead
1969-
// then generate new subaddress pubkeys and add them to m_subaddresses table
1970-
for (uint32_t i = m_subaddress_labels.size()+m_subaddress_lookahead_major; i < m_subaddress_labels.size()+major; i++) { // m_subaddress_labels are the accounts the user is conciously keeping track of. We want that number plus the lookahead major accounts in our key table
1971-
for (uint32_t j = 0; j < minor; j++) { // these are newly made accounts, minor index will start from zero
1972-
cryptonote::subaddress_index idx = {i,j};
1973-
create_one_off_subaddress(idx); // then generate the key and add it to the table
1974-
}
1975-
}
1976-
}
1977-
if (minor > m_subaddress_lookahead_minor) { // if increasing the minor lookahead we need to also go back and expand the existing accounts
1978-
for (uint32_t i = 0; i < m_subaddress_labels.size()+m_subaddress_lookahead_major; i++) {
1979-
uint32_t minor_idx_start = i < m_subaddress_labels.size() ? m_subaddress_labels[i].size()+m_subaddress_lookahead_minor : m_subaddress_lookahead_minor; // if there are existing minor indices being tracked under this account we need to account for that
1980-
for (uint32_t j = minor_idx_start; j < minor; j++) {
1981-
cryptonote::subaddress_index idx = {i,j};
1982-
create_one_off_subaddress(idx);
1983-
}
1984-
}
1985-
}
1968+
const uint32_t old_major_lookahead = m_subaddress_lookahead_major;
1969+
const uint32_t old_minor_lookahead = m_subaddress_lookahead_minor;
1970+
19861971
m_subaddress_lookahead_major = major;
19871972
m_subaddress_lookahead_minor = minor;
1973+
1974+
if (old_major_lookahead >= major && old_minor_lookahead >= minor)
1975+
return;
1976+
1977+
// Expand the subaddresses map so that outputs received to the higher lookaheads will be identified in the scan loop
1978+
hw::device &hwdev = m_account.get_device();
1979+
cryptonote::subaddress_index index2;
1980+
const uint32_t max_major_idx = this->get_num_subaddress_accounts() > 0 ? (this->get_num_subaddress_accounts() - 1) : 0;
1981+
const uint32_t major_end = get_subaddress_clamped_sum(max_major_idx, major);
1982+
for (index2.major = 0; index2.major < major_end; ++index2.major)
1983+
{
1984+
// The existing minor addresses already set for this account
1985+
const uint32_t n_minor_subaddrs = this->get_num_subaddresses(index2.major);
1986+
1987+
// The subaddress lookahead is expected to expand from the max index in expand_subaddresses
1988+
const uint32_t max_minor_idx = n_minor_subaddrs > 0 ? (n_minor_subaddrs - 1) : 0;
1989+
const uint32_t begin = (n_minor_subaddrs || index2.major < old_major_lookahead) ? get_subaddress_clamped_sum(max_minor_idx, old_minor_lookahead) : 0;
1990+
// The expected new n minor subaddresses allocated for this account
1991+
const uint32_t end = get_subaddress_clamped_sum(max_minor_idx, minor);
1992+
1993+
if (begin >= end)
1994+
continue;
1995+
1996+
const std::vector<crypto::public_key> pkeys = hwdev.get_subaddress_spend_public_keys(m_account.get_keys(), index2.major, begin, end);
1997+
for (index2.minor = begin; index2.minor < end; ++index2.minor)
1998+
{
1999+
const crypto::public_key &D = pkeys.at(index2.minor - begin);
2000+
m_subaddresses[D] = index2;
2001+
}
2002+
}
19882003
}
19892004
//----------------------------------------------------------------------------------------------------
19902005
/*!

src/wallet/wallet_rpc_server.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -653,11 +653,24 @@ namespace tools
653653
bool wallet_rpc_server::on_set_subaddr_lookahead(const wallet_rpc::COMMAND_RPC_SET_SUBADDR_LOOKAHEAD::request& req, wallet_rpc::COMMAND_RPC_SET_SUBADDR_LOOKAHEAD::response& res, epee::json_rpc::error& er, const connection_context *ctx)
654654
{
655655
if (!m_wallet) return not_open(er);
656-
try {
657-
m_wallet->set_subaddress_lookahead(req.major_idx, req.minor_idx);
656+
CHECK_IF_BACKGROUND_SYNCING();
657+
const std::string wallet_file = m_wallet->get_wallet_file();
658+
if (wallet_file == "" || m_wallet->verify_password(req.password))
659+
{
660+
try
661+
{
662+
m_wallet->set_subaddress_lookahead(req.major_idx, req.minor_idx);
663+
m_wallet->rewrite(wallet_file, req.password);
664+
}
665+
catch (const std::exception& e) {
666+
handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
667+
return false;
668+
}
658669
}
659-
catch (const std::exception& e) {
660-
handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
670+
else
671+
{
672+
er.code = WALLET_RPC_ERROR_CODE_INVALID_PASSWORD;
673+
er.message = "Invalid password.";
661674
return false;
662675
}
663676
return true;

src/wallet/wallet_rpc_server_commands_defs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,11 @@ namespace wallet_rpc
186186
{
187187
struct request_t
188188
{
189+
std::string password;
189190
uint64_t major_idx;
190191
uint64_t minor_idx;
191192
BEGIN_KV_SERIALIZE_MAP()
193+
KV_SERIALIZE(password)
192194
KV_SERIALIZE(major_idx)
193195
KV_SERIALIZE(minor_idx)
194196
END_KV_SERIALIZE_MAP()

tests/unit_tests/subaddress.cpp

Lines changed: 93 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,36 @@ TEST_F(WalletSubaddress, OutOfBoundsIndexes)
103103
}
104104
}
105105

106-
TEST_F(WalletSubaddress, ExpandPubkeyTable)
106+
// Helper function to check max subaddrs allocated
107+
static void check_expected_max(const tools::wallet2 &w1, const cryptonote::subaddress_index exp_max)
107108
{
108-
// these test assume we are starting with the default setup state
109+
for (uint32_t i = 0; i <= exp_max.minor; ++i)
110+
{
111+
auto subaddr = w1.get_subaddress({exp_max.major, i});
112+
EXPECT_NE(boost::none, w1.get_subaddress_index(subaddr));
113+
}
114+
auto subaddr = w1.get_subaddress({exp_max.major, exp_max.minor + 1});
115+
EXPECT_EQ(boost::none, w1.get_subaddress_index(subaddr));
116+
};
117+
118+
static void expect_default_wallet_state(const tools::wallet2 &w1)
119+
{
120+
// these tests assume we are starting with the default setup state
109121
EXPECT_EQ(2, w1.get_num_subaddress_accounts());
110122
EXPECT_EQ(50, w1.get_subaddress_lookahead().first);
111123
EXPECT_EQ(200, w1.get_subaddress_lookahead().second);
124+
125+
// We assume we start with subaddrs for minor indexes 0 to 199
126+
check_expected_max(w1, {0,199});
127+
check_expected_max(w1, {1,199});
128+
check_expected_max(w1, {49,199});
129+
check_expected_max(w1, {50,199}); // 50 because the test starts with accounts 0 and 1 already allocated
130+
EXPECT_EQ(boost::none, w1.get_subaddress_index(w1.get_subaddress({51,0})));
131+
}
132+
133+
TEST_F(WalletSubaddress, SetLookahead)
134+
{
135+
expect_default_wallet_state(w1);
112136
// get_subaddress_index looks up keys in the private m_subaddresses dictionary so we will use it to test if a key is properly being scanned for
113137
cryptonote::subaddress_index test_idx = {50, 199};
114138
auto subaddr = w1.get_subaddress(test_idx);
@@ -117,14 +141,75 @@ TEST_F(WalletSubaddress, ExpandPubkeyTable)
117141
w1.set_subaddress_lookahead(100, 200);
118142
EXPECT_EQ(100, w1.get_subaddress_lookahead().first);
119143
EXPECT_EQ(200, w1.get_subaddress_lookahead().second);
120-
test_idx = {100, 199};
121-
subaddr = w1.get_subaddress(test_idx);
122-
EXPECT_NE(boost::none, w1.get_subaddress_index(subaddr));
144+
check_expected_max(w1, {100, 199});
123145
// next test expanding the minor lookahead
124146
w1.set_subaddress_lookahead(100, 300);
125147
EXPECT_EQ(100, w1.get_subaddress_lookahead().first);
126148
EXPECT_EQ(300, w1.get_subaddress_lookahead().second);
127-
test_idx = {100, 299};
128-
subaddr = w1.get_subaddress(test_idx);
129-
EXPECT_NE(boost::none, w1.get_subaddress_index(subaddr));
149+
check_expected_max(w1, {100, 299});
150+
}
151+
152+
TEST_F(WalletSubaddress, ExpandThenSetMinorIncreaseOnly)
153+
{
154+
expect_default_wallet_state(w1);
155+
156+
// Mock receive to {0,150}, so expand from there
157+
w1.expand_subaddresses({0,150});
158+
// We should now have subaddresses for minor indexes 0 to 349
159+
check_expected_max(w1, {0,349});
160+
check_expected_max(w1, {1,199});
161+
check_expected_max(w1, {49,199});
162+
check_expected_max(w1, {50,199});
163+
EXPECT_EQ(boost::none, w1.get_subaddress_index(w1.get_subaddress({51,0})));
164+
165+
// Now set the minor lookahead 100 higher
166+
w1.set_subaddress_lookahead(50, 200+100);
167+
// We should have subaddresses for minor indexes 0 to 449
168+
check_expected_max(w1, {0,449});
169+
check_expected_max(w1, {1,299});
170+
check_expected_max(w1, {49,299});
171+
check_expected_max(w1, {50,299});
172+
EXPECT_EQ(boost::none, w1.get_subaddress_index(w1.get_subaddress({51,0})));
173+
}
174+
175+
TEST_F(WalletSubaddress, ExpandThenSetMajorIncreaseOnly)
176+
{
177+
expect_default_wallet_state(w1);
178+
179+
// Mock receive to {40,0}, so expand from there
180+
w1.expand_subaddresses({40,0});
181+
check_expected_max(w1, {0,199});
182+
check_expected_max(w1, {1,199});
183+
check_expected_max(w1, {40,199});
184+
check_expected_max(w1, {89,199});
185+
EXPECT_EQ(boost::none, w1.get_subaddress_index(w1.get_subaddress({90,0})));
186+
187+
// Now set the major lookahead 10 higher
188+
w1.set_subaddress_lookahead(50+10, 200);
189+
check_expected_max(w1, {0,199});
190+
check_expected_max(w1, {1,199});
191+
check_expected_max(w1, {40,199});
192+
check_expected_max(w1, {99,199});
193+
EXPECT_EQ(boost::none, w1.get_subaddress_index(w1.get_subaddress({100,0})));
194+
}
195+
196+
TEST_F(WalletSubaddress, ExpandThenSetIncreaseBoth)
197+
{
198+
expect_default_wallet_state(w1);
199+
200+
// Mock receive to {40,150}, so expand from there
201+
w1.expand_subaddresses({40,150});
202+
check_expected_max(w1, {0,199});
203+
check_expected_max(w1, {1,199});
204+
check_expected_max(w1, {40,349});
205+
check_expected_max(w1, {89,199});
206+
EXPECT_EQ(boost::none, w1.get_subaddress_index(w1.get_subaddress({90,0})));
207+
208+
// Now set the major lookahead 10 higher and minor 100 higher
209+
w1.set_subaddress_lookahead(50+10, 200+100);
210+
check_expected_max(w1, {0,299});
211+
check_expected_max(w1, {1,299});
212+
check_expected_max(w1, {40,449});
213+
check_expected_max(w1, {99,299});
214+
EXPECT_EQ(boost::none, w1.get_subaddress_index(w1.get_subaddress({100,0})));
130215
}

utils/python-rpc/framework/wallet.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,11 +333,12 @@ def generate_from_keys(self, restore_height = 0, filename = "", password = "", a
333333
}
334334
return self.rpc.send_json_rpc_request(generate_from_keys)
335335

336-
def set_subaddress_lookahead(self, major_idx: int, minor_idx: int):
336+
def set_subaddress_lookahead(self, major_idx: int, minor_idx: int, password = ""):
337337
lookahead = {
338338
'method': 'set_subaddress_lookahead',
339339
'jsonrpc': '2.0',
340340
'params' : {
341+
'password': password,
341342
'major_idx': major_idx,
342343
'minor_idx': minor_idx
343344
},

0 commit comments

Comments
 (0)