diff options
| author | tobtoht <tob@featherwallet.org> | 2026-06-16 11:06:52 +0000 |
|---|---|---|
| committer | tobtoht <tob@featherwallet.org> | 2026-06-16 11:06:52 +0000 |
| commit | fc46361376a6f3a604df7ae84ddda848d43dd6cc (patch) | |
| tree | 17848b3d21a1370869534e08b83a2e45addf386f | |
| parent | 4fbb0b22e02c46e20da3d35ec6b28c6fdfaa3912 (diff) | |
| parent | d6f0a4d211cc419095e778a39a83d65000e47bf6 (diff) | |
| download | monzero-core-fc46361376a6f3a604df7ae84ddda848d43dd6cc.tar.gz monzero-core-fc46361376a6f3a604df7ae84ddda848d43dd6cc.tar.xz monzero-core-fc46361376a6f3a604df7ae84ddda848d43dd6cc.zip | |
Merge pull request #10754
d6f0a4d wallet2: store multisig nonce erasure before returning signed txset (selsta)
ACKs: UkoeHB, tobtoht
| -rw-r--r-- | src/wallet/wallet2.cpp | 71 | ||||
| -rw-r--r-- | src/wallet/wallet2.h | 3 |
2 files changed, 48 insertions, 26 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 61f49481e..e2aa2b201 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -8155,14 +8155,6 @@ std::string wallet2::save_multisig_tx(multisig_tx_set txs) { LOG_PRINT_L0("saving " << txs.m_ptx.size() << " multisig transactions"); - // txes generated, get rid of used k values - for (size_t n = 0; n < txs.m_ptx.size(); ++n) - for (size_t idx: txs.m_ptx[n].construction_data.selected_transfers) - { - memwipe(m_transfers[idx].m_multisig_k.data(), m_transfers[idx].m_multisig_k.size() * sizeof(m_transfers[idx].m_multisig_k[0])); - m_transfers[idx].m_multisig_k.clear(); - } - // zero out some data we don't want to share for (auto &ptx: txs.m_ptx) { @@ -8190,6 +8182,11 @@ std::string wallet2::save_multisig_tx(multisig_tx_set txs) } LOG_PRINT_L2("Saving multisig unsigned tx data: " << oss.str()); std::string ciphertext = encrypt_with_view_secret_key(oss.str()); + + // The transaction creator has already signed, so do not expose the txset + // until the corresponding one-time nonce erasure is stored. + clear_multisig_k_and_store(txs); + return std::string(MULTISIG_UNSIGNED_TX_PREFIX) + ciphertext; } //---------------------------------------------------------------------------------------------------- @@ -8348,8 +8345,12 @@ bool wallet2::load_multisig_tx_from_file(const std::string &filename, multisig_t return true; } //---------------------------------------------------------------------------------------------------- -bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto::hash> &txids) +bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs_inout, std::vector<crypto::hash> &txids) { + multisig_tx_set exported_txs = exported_txs_inout; + std::vector<crypto::hash> signed_txids; + std::vector<std::pair<crypto::hash, size_t>> signed_tx_key_indices; + THROW_WALLET_EXCEPTION_IF(exported_txs.m_ptx.empty(), error::wallet_internal_error, "No tx found"); const crypto::public_key local_signer = get_multisig_signer_public_key(); @@ -8363,8 +8364,6 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto THROW_WALLET_EXCEPTION_IF(frozen(exported_txs), error::wallet_internal_error, "Will not sign multisig tx containing frozen outputs") - txids.clear(); - // The 'exported_txs' contains a set of different transactions for the multisig group to try to sign. Each of those // transactions has a set of 'signing attempts' corresponding to all the possible signing groups within the multisig. // - Here, we will partially sign as many of those signing attempts as possible, for each proposed transaction. @@ -8474,25 +8473,26 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto "Unable to finalize the transaction: the ignore sets for these tx attempts seem to be malformed."); const crypto::hash txid = get_transaction_hash(ptx.tx); if (store_tx_info()) - { - m_tx_keys[txid] = ptx.tx_key; - m_additional_tx_keys[txid] = ptx.additional_tx_keys; - } - txids.push_back(txid); + signed_tx_key_indices.emplace_back(txid, n); + signed_txids.push_back(txid); } } - // signatures generated, get rid of any unused k values (must do export_multisig() to make more tx attempts with the - // inputs in the transactions worked on here) - for (size_t n = 0; n < exported_txs.m_ptx.size(); ++n) - for (size_t idx: exported_txs.m_ptx[n].construction_data.selected_transfers) - { - memwipe(m_transfers[idx].m_multisig_k.data(), m_transfers[idx].m_multisig_k.size() * sizeof(m_transfers[idx].m_multisig_k[0])); - m_transfers[idx].m_multisig_k.clear(); - } + exported_txs.m_signers.insert(local_signer); - exported_txs.m_signers.insert(get_multisig_signer_public_key()); + // Do not expose signatures until all nonce material for the selected inputs + // has been erased from the wallet cache. + clear_multisig_k_and_store(exported_txs); + for (const auto &entry: signed_tx_key_indices) + { + const auto &ptx = exported_txs.m_ptx[entry.second]; + m_tx_keys[entry.first] = ptx.tx_key; + m_additional_tx_keys[entry.first] = ptx.additional_tx_keys; + } + + exported_txs_inout = std::move(exported_txs); + txids = std::move(signed_txids); return true; } //---------------------------------------------------------------------------------------------------- @@ -15223,6 +15223,27 @@ void wallet2::get_multisig_k(size_t idx, const std::unordered_set<rct::key> &use THROW_WALLET_EXCEPTION(tools::error::multisig_export_needed); } //---------------------------------------------------------------------------------------------------- +void wallet2::clear_multisig_k_and_store(const multisig_tx_set &txs) +{ + // Must succeed before any txset produced with these nonces is exposed. + bool changed = false; + for (const auto &ptx: txs.m_ptx) + { + for (size_t idx: ptx.construction_data.selected_transfers) + { + std::vector<rct::key> &multisig_k = m_transfers[idx].m_multisig_k; + if (multisig_k.empty()) + continue; + memwipe(multisig_k.data(), multisig_k.size() * sizeof(multisig_k[0])); + multisig_k.clear(); + changed = true; + } + } + + if (changed) + store(); +} +//---------------------------------------------------------------------------------------------------- rct::multisig_kLRki wallet2::get_multisig_kLRki(size_t n, const rct::key &k) const { CHECK_AND_ASSERT_THROW_MES(n < m_transfers.size(), "Bad m_transfers index"); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index cedfecbeb..074755002 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1211,7 +1211,7 @@ private: bool load_multisig_tx(cryptonote::blobdata blob, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL); bool load_multisig_tx_from_file(const std::string &filename, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL); bool sign_multisig_tx_from_file(const std::string &filename, std::vector<crypto::hash> &txids, std::function<bool(const multisig_tx_set&)> accept_func); - bool sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto::hash> &txids); + bool sign_multisig_tx(multisig_tx_set &exported_txs_inout, std::vector<crypto::hash> &txids); bool sign_multisig_tx_to_file(multisig_tx_set &exported_txs, const std::string &filename, std::vector<crypto::hash> &txids); std::vector<pending_tx> create_unmixable_sweep_transactions(); void discard_unmixable_outputs(); @@ -1908,6 +1908,7 @@ private: rct::multisig_kLRki get_multisig_composite_kLRki(size_t n, const std::unordered_set<crypto::public_key> &ignore_set, std::unordered_set<rct::key> &used_L, std::unordered_set<rct::key> &new_used_L) const; rct::multisig_kLRki get_multisig_kLRki(size_t n, const rct::key &k) const; void get_multisig_k(size_t idx, const std::unordered_set<rct::key> &used_L, rct::key &nonce); + void clear_multisig_k_and_store(const multisig_tx_set &txs); void update_multisig_rescan_info(const std::vector<std::vector<rct::key>> &multisig_k, const std::vector<std::vector<tools::wallet2::multisig_info>> &info, size_t n); bool add_rings(const crypto::chacha_key &key, const cryptonote::transaction_prefix &tx); bool add_rings(const cryptonote::transaction_prefix &tx); |
