mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-12 13:42:10 +02:00
- Rename arg_state to new_state in Update() declaration to match implementation - Replace RecomputeCanonical manual loop with std::ranges::min_element - Add variant txid validation in the deserialize constructor - Make Init() private and have it clear all members including m_txs Co-authored-by: Anthony Towns <aj@erisian.com.au>
112 lines
3.8 KiB
C++
112 lines
3.8 KiB
C++
// Copyright (c) 2021-present The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include <wallet/transaction.h>
|
|
|
|
#include <consensus/validation.h>
|
|
#include <interfaces/chain.h>
|
|
|
|
using interfaces::FoundBlock;
|
|
|
|
namespace wallet {
|
|
bool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const
|
|
{
|
|
CMutableTransaction tx1 {*this->GetTx()};
|
|
CMutableTransaction tx2 {*_tx.GetTx()};
|
|
for (auto& txin : tx1.vin) {
|
|
txin.scriptSig = CScript();
|
|
txin.scriptWitness.SetNull();
|
|
}
|
|
for (auto& txin : tx2.vin) {
|
|
txin.scriptSig = CScript();
|
|
txin.scriptWitness.SetNull();
|
|
}
|
|
return CTransaction(tx1) == CTransaction(tx2);
|
|
}
|
|
|
|
bool CWalletTx::InMempool() const
|
|
{
|
|
return state<TxStateInMempool>();
|
|
}
|
|
|
|
int64_t CWalletTx::GetTxTime() const
|
|
{
|
|
int64_t n = nTimeSmart;
|
|
return n ? n : nTimeReceived;
|
|
}
|
|
|
|
void CWalletTx::updateState(interfaces::Chain& chain)
|
|
{
|
|
bool active;
|
|
auto lookup_block = [&](const uint256& hash, int& height, TxState& state) {
|
|
// If tx block (or conflicting block) was reorged out of chain
|
|
// while the wallet was shutdown, change tx status to UNCONFIRMED
|
|
// and reset block height, hash, and index. ABANDONED tx don't have
|
|
// associated blocks and don't need to be updated. The case where a
|
|
// transaction was reorged out while online and then reconfirmed
|
|
// while offline is covered by the rescan logic.
|
|
if (!chain.findBlock(hash, FoundBlock().inActiveChain(active).height(height)) || !active) {
|
|
state = TxStateInactive{};
|
|
}
|
|
};
|
|
if (auto* conf = state<TxStateConfirmed>()) {
|
|
lookup_block(conf->confirmed_block_hash, conf->confirmed_block_height, m_state);
|
|
} else if (auto* conf = state<TxStateBlockConflicted>()) {
|
|
lookup_block(conf->conflicting_block_hash, conf->conflicting_block_height, m_state);
|
|
}
|
|
|
|
// If the above downgraded a previously-confirmed witness variant back to unconfirmed,
|
|
// the canonical choice is no longer pinned by confirmation. Re-apply the least-weight rule.
|
|
if (!isConfirmed()) RecomputeCanonical();
|
|
}
|
|
|
|
bool CWalletTx::Update(CTransactionRef new_tx, const TxState& new_state)
|
|
{
|
|
Assert(new_tx);
|
|
if (!Assume(GetHash() == new_tx->GetHash())) {
|
|
return false;
|
|
}
|
|
bool ret = false;
|
|
const auto& [tx_pair, inserted] = m_txs.emplace(new_tx->GetWitnessHash(), std::move(new_tx));
|
|
if (inserted) {
|
|
ret = true;
|
|
}
|
|
const auto& [wtxid, tx] = *tx_pair;
|
|
|
|
if (new_state.index() != m_state.index()) {
|
|
m_state = new_state;
|
|
if (state<TxStateConfirmed>()) {
|
|
m_canonical_wtxid = wtxid;
|
|
}
|
|
ret = true;
|
|
} else {
|
|
assert(TxStateSerializedIndex(m_state) == TxStateSerializedIndex(new_state));
|
|
assert(TxStateSerializedBlockHash(m_state) == TxStateSerializedBlockHash(new_state));
|
|
}
|
|
|
|
// While unconfirmed, derive the canonical variant from all known variants
|
|
if (!isConfirmed()) {
|
|
const Wtxid prev_canonical = m_canonical_wtxid;
|
|
RecomputeCanonical();
|
|
if (m_canonical_wtxid != prev_canonical) {
|
|
ret = true;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void CWalletTx::RecomputeCanonical()
|
|
{
|
|
// Recompute the canonical variant among the witness variants. They share
|
|
// the txid but differ in the wtxid. Prefer variant with witness data and
|
|
// the least weight.
|
|
Assert(!m_txs.empty());
|
|
|
|
m_canonical_wtxid = std::ranges::min_element(m_txs, std::less{}, [](const auto& entry) {
|
|
return std::make_pair(!entry.second->HasWitness(), GetTransactionWeight(*entry.second));
|
|
})->first;
|
|
}
|
|
} // namespace wallet
|