wallet2: keep multisig import state consistent across failed refreshes

(cherry picked from commit b2cd62754bfb837bad2a49ea5b3808f35bb6a16c)
This commit is contained in:
woodser
2026-10-04 18:50:22 -04:00
committed by such-gitea
parent 10ade8c591
commit a1553381a2
+21 -10
View File
@@ -15419,9 +15419,12 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs, bool re
{
CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig");
if (!m_multisig_rescan_k.empty() && !m_multisig_rescan_info.empty())
// consume pending rescan state from a prior import if refreshing
if (refresh_after_import && !m_multisig_rescan_k.empty() && !m_multisig_rescan_info.empty())
refresh(false);
// parse and validate locally so failures preserve any pending rescan state
std::vector<std::vector<tools::wallet2::multisig_info>> info;
std::unordered_set<crypto::public_key> seen;
for (cryptonote::blobdata &data: blobs)
{
@@ -15487,18 +15490,20 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs, bool re
}
MINFO(boost::format("%u outputs found") % boost::lexical_cast<std::string>(i.size()));
m_multisig_rescan_info.push_back(std::move(i));
info.push_back(std::move(i));
}
CHECK_AND_ASSERT_THROW_MES(m_multisig_rescan_info.size() + 1 <= m_multisig_signers.size() && m_multisig_rescan_info.size() + 1 >= m_multisig_threshold, "Wrong number of multisig sources");
CHECK_AND_ASSERT_THROW_MES(info.size() + 1 <= m_multisig_signers.size() && info.size() + 1 >= m_multisig_threshold, "Wrong number of multisig sources");
m_multisig_rescan_k.reserve(m_transfers.size());
std::vector<std::vector<rct::key>> k;
auto wiper = epee::misc_utils::create_scope_leave_handler([&](){for (auto &v: k) memwipe(v.data(), v.size() * sizeof(v[0]));});
k.reserve(m_transfers.size());
for (const auto &td: m_transfers)
m_multisig_rescan_k.push_back(td.m_multisig_k);
k.push_back(td.m_multisig_k);
// how many outputs we're going to update
size_t n_outputs = m_transfers.size();
for (const auto &pi: m_multisig_rescan_info)
for (const auto &pi: info)
if (pi.size() < n_outputs)
n_outputs = pi.size();
@@ -15506,7 +15511,7 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs, bool re
return 0;
// check signers are consistent
for (const auto &pi: m_multisig_rescan_info)
for (const auto &pi: info)
{
CHECK_AND_ASSERT_THROW_MES(std::find(m_multisig_signers.begin(), m_multisig_signers.end(), pi[0].m_signer) != m_multisig_signers.end(),
"Signer is not a member of this multisig wallet");
@@ -15515,15 +15520,21 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs, bool re
}
// trim data we don't have info for from all participants
for (auto &pi: m_multisig_rescan_info)
for (auto &pi: info)
pi.resize(n_outputs);
// sort by signer
if (!m_multisig_rescan_info.empty() && !m_multisig_rescan_info.front().empty())
if (!info.empty() && !info.front().empty())
{
std::sort(m_multisig_rescan_info.begin(), m_multisig_rescan_info.end(), [](const std::vector<tools::wallet2::multisig_info> &i0, const std::vector<tools::wallet2::multisig_info> &i1){ return memcmp(&i0[0].m_signer, &i1[0].m_signer, sizeof(i0[0].m_signer)) < 0; });
std::sort(info.begin(), info.end(), [](const std::vector<tools::wallet2::multisig_info> &i0, const std::vector<tools::wallet2::multisig_info> &i1){ return memcmp(&i0[0].m_signer, &i1[0].m_signer, sizeof(i0[0].m_signer)) < 0; });
}
// wipe prior pending rescan state and install its replacement only after full validation
for (auto &v: m_multisig_rescan_k)
memwipe(v.data(), v.size() * sizeof(v[0]));
m_multisig_rescan_info = std::move(info);
m_multisig_rescan_k = std::move(k);
// first pass to determine where to detach the blockchain
for (size_t n = 0; n < n_outputs; ++n)
{