wallet: reset output spent status on blockchain reorg

If the blockchain gets reorganized, all outputs spent in the part
of the blockchain that's blown away need to be reset to unspent
(they may end up spent again on the blocks that replace the blocks
that are removed, however).
This commit is contained in:
moneromooo-monero 2016-06-16 23:58:54 +01:00
parent 73d59f17e1
commit 089df4af83
No known key found for this signature in database
GPG key ID: 686F07454D6CEFC3
2 changed files with 61 additions and 11 deletions

View file

@ -490,6 +490,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << get_transaction_hash(tx)); LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << get_transaction_hash(tx));
tx_money_spent_in_ins += amount; tx_money_spent_in_ins += amount;
td.m_spent = true; td.m_spent = true;
td.m_spent_height = height;
if (0 != m_callback) if (0 != m_callback)
m_callback->on_money_spent(height, td.m_tx, amount, tx); m_callback->on_money_spent(height, td.m_tx, amount, tx);
} }
@ -1167,6 +1168,17 @@ void wallet2::detach_blockchain(uint64_t height)
LOG_PRINT_L0("Detaching blockchain on height " << height); LOG_PRINT_L0("Detaching blockchain on height " << height);
size_t transfers_detached = 0; size_t transfers_detached = 0;
for (size_t i = 0; i < m_transfers.size(); ++i)
{
wallet2::transfer_details &td = m_transfers[i];
if (td.m_spent && td.m_spent_height >= height)
{
LOG_PRINT_L1("Resetting spent status for output " << i << ": " << td.m_key_image);
td.m_spent = false;
td.m_spent_height = 0;
}
}
auto it = std::find_if(m_transfers.begin(), m_transfers.end(), [&](const transfer_details& td){return td.m_block_height >= height;}); auto it = std::find_if(m_transfers.begin(), m_transfers.end(), [&](const transfer_details& td){return td.m_block_height >= height;});
size_t i_start = it - m_transfers.begin(); size_t i_start = it - m_transfers.begin();
@ -1980,10 +1992,12 @@ void wallet2::rescan_spent()
if (td.m_spent) if (td.m_spent)
{ {
LOG_PRINT_L0("Marking output " << i << "(" << td.m_key_image << ") as unspent, it was marked as spent"); LOG_PRINT_L0("Marking output " << i << "(" << td.m_key_image << ") as unspent, it was marked as spent");
td.m_spent_height = 0;
} }
else else
{ {
LOG_PRINT_L0("Marking output " << i << "(" << td.m_key_image << ") as spent, it was marked as unspent"); LOG_PRINT_L0("Marking output " << i << "(" << td.m_key_image << ") as spent, it was marked as unspent");
// unknown height, if this gets reorged, it might still be missed
} }
td.m_spent = daemon_resp.spent_status[i] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT; td.m_spent = daemon_resp.spent_status[i] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT;
} }
@ -2293,7 +2307,10 @@ void wallet2::commit_tx(pending_tx& ptx)
LOG_PRINT_L2("transaction " << txid << " generated ok and sent to daemon, key_images: [" << ptx.key_images << "]"); LOG_PRINT_L2("transaction " << txid << " generated ok and sent to daemon, key_images: [" << ptx.key_images << "]");
BOOST_FOREACH(transfer_container::iterator it, ptx.selected_transfers) BOOST_FOREACH(transfer_container::iterator it, ptx.selected_transfers)
{
it->m_spent = true; it->m_spent = true;
it->m_spent_height = 0;
}
//fee includes dust if dust policy specified it. //fee includes dust if dust policy specified it.
LOG_PRINT_L0("Transaction successfully sent. <" << txid << ">" << ENDL LOG_PRINT_L0("Transaction successfully sent. <" << txid << ">" << ENDL
@ -2371,7 +2388,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
// mark transfers to be used as "spent" // mark transfers to be used as "spent"
BOOST_FOREACH(transfer_container::iterator it, ptx.selected_transfers) BOOST_FOREACH(transfer_container::iterator it, ptx.selected_transfers)
{
it->m_spent = true; it->m_spent = true;
it->m_spent_height = 0;
}
} }
// if we made it this far, we've selected our transactions. committing them will mark them spent, // if we made it this far, we've selected our transactions. committing them will mark them spent,
@ -2381,7 +2401,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
{ {
// mark transfers to be used as not spent // mark transfers to be used as not spent
BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers) BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers)
{
it2->m_spent = false; it2->m_spent = false;
it2->m_spent_height = 0;
}
} }
@ -2398,8 +2421,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
{ {
// mark transfers to be used as not spent // mark transfers to be used as not spent
BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers) BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers)
{
it2->m_spent = false; it2->m_spent = false;
it2->m_spent_height = 0;
}
} }
if (attempt_count >= MAX_SPLIT_ATTEMPTS) if (attempt_count >= MAX_SPLIT_ATTEMPTS)
@ -2416,8 +2441,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
{ {
// mark transfers to be used as not spent // mark transfers to be used as not spent
BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers) BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers)
{
it2->m_spent = false; it2->m_spent = false;
it2->m_spent_height = 0;
}
} }
throw; throw;
@ -3860,7 +3887,10 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo
// mark transfers to be used as "spent" // mark transfers to be used as "spent"
BOOST_FOREACH(transfer_container::iterator it, ptx.selected_transfers) BOOST_FOREACH(transfer_container::iterator it, ptx.selected_transfers)
{
it->m_spent = true; it->m_spent = true;
it->m_spent_height = 0;
}
} }
// if we made it this far, we've selected our transactions. committing them will mark them spent, // if we made it this far, we've selected our transactions. committing them will mark them spent,
@ -3870,8 +3900,10 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo
{ {
// mark transfers to be used as not spent // mark transfers to be used as not spent
BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers) BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers)
{
it2->m_spent = false; it2->m_spent = false;
it2->m_spent_height = 0;
}
} }
// if we made it this far, we're OK to actually send the transactions // if we made it this far, we're OK to actually send the transactions
@ -3887,8 +3919,10 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo
{ {
// mark transfers to be used as not spent // mark transfers to be used as not spent
BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers) BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers)
{
it2->m_spent = false; it2->m_spent = false;
it2->m_spent_height = 0;
}
} }
if (attempt_count >= MAX_SPLIT_ATTEMPTS) if (attempt_count >= MAX_SPLIT_ATTEMPTS)
@ -3905,8 +3939,10 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo
{ {
// mark transfers to be used as not spent // mark transfers to be used as not spent
BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers) BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers)
{
it2->m_spent = false; it2->m_spent = false;
it2->m_spent_height = 0;
}
} }
throw; throw;

View file

@ -103,6 +103,7 @@ namespace tools
size_t m_internal_output_index; size_t m_internal_output_index;
uint64_t m_global_output_index; uint64_t m_global_output_index;
bool m_spent; bool m_spent;
uint64_t m_spent_height;
crypto::key_image m_key_image; //TODO: key_image stored twice :( crypto::key_image m_key_image; //TODO: key_image stored twice :(
rct::key m_mask; rct::key m_mask;
uint64_t m_amount; uint64_t m_amount;
@ -490,7 +491,7 @@ namespace tools
}; };
} }
BOOST_CLASS_VERSION(tools::wallet2, 13) BOOST_CLASS_VERSION(tools::wallet2, 13)
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 1) BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 2)
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 1) BOOST_CLASS_VERSION(tools::wallet2::payment_details, 1)
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 4) BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 4)
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 2) BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 2)
@ -500,14 +501,21 @@ namespace boost
namespace serialization namespace serialization
{ {
template <class Archive> template <class Archive>
inline void initialize_transfer_details(Archive &a, tools::wallet2::transfer_details &x) inline void initialize_transfer_details(Archive &a, tools::wallet2::transfer_details &x, const boost::serialization::version_type ver)
{ {
} }
template<> template<>
inline void initialize_transfer_details(boost::archive::binary_iarchive &a, tools::wallet2::transfer_details &x) inline void initialize_transfer_details(boost::archive::binary_iarchive &a, tools::wallet2::transfer_details &x, const boost::serialization::version_type ver)
{ {
x.m_mask = rct::identity(); if (ver < 1)
x.m_amount = x.m_tx.vout[x.m_internal_output_index].amount; {
x.m_mask = rct::identity();
x.m_amount = x.m_tx.vout[x.m_internal_output_index].amount;
}
if (ver < 2)
{
x.m_spent_height = 0;
}
} }
template <class Archive> template <class Archive>
@ -522,11 +530,17 @@ namespace boost
if (ver < 1) if (ver < 1)
{ {
// ensure mask and amount are set // ensure mask and amount are set
initialize_transfer_details(a, x); initialize_transfer_details(a, x, ver);
return; return;
} }
a & x.m_mask; a & x.m_mask;
a & x.m_amount; a & x.m_amount;
if (ver < 2)
{
initialize_transfer_details(a, x, ver);
return;
}
a & x.m_spent_height;
} }
template <class Archive> template <class Archive>