Skip to content

Commit e069c23

Browse files
committed
Segwit light for spending coins.
Handle segwit-light properly when spending coins, either for staking or normal transactions in the wallet. Depending on when a transaction was confirmed, we need to make sure to include the right outpoint (with txid or bare txid) in the transaction spending an output.
1 parent 8d49fbb commit e069c23

10 files changed

+68
-30
lines changed

divi/src/BlockMemoryPoolTransactionCollector.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ std::vector<TxPriority> BlockMemoryPoolTransactionCollector::PrioritizeMempoolTr
202202
// Read prev transaction
203203
if (!view.HaveCoins(txin.prevout.hash)) {
204204
CTransaction prevTx;
205-
if(!mempool_.lookup(txin.prevout.hash, prevTx)) {
205+
if(!mempool_.lookupOutpoint(txin.prevout.hash, prevTx)) {
206206
// This should never happen; all transactions in the memory
207207
// pool should connect to either transactions in the chain
208208
// or other transactions in the memory pool.

divi/src/PoSTransactionCreator.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ bool PoSTransactionCreator::SetSuportedStakingScript(
133133
const StakableCoin& stakableCoin,
134134
CMutableTransaction& txNew)
135135
{
136-
txNew.vin.push_back(CTxIn(stakableCoin.tx->GetHash(), stakableCoin.outputIndex));
137-
txNew.vout.push_back(CTxOut(0, stakableCoin.tx->vout[stakableCoin.outputIndex].scriptPubKey ));
136+
txNew.vin.emplace_back(stakableCoin.tx->GetHashForSpendingOutput(), stakableCoin.outputIndex);
137+
txNew.vout.emplace_back(0, stakableCoin.tx->vout[stakableCoin.outputIndex].scriptPubKey);
138138

139139
return true;
140140
}
@@ -150,7 +150,7 @@ void PoSTransactionCreator::CombineUtxos(
150150
for(const StakableCoin& pcoin : stakedCoins_->asSet())
151151
{
152152
if(pcoin.tx->vout[pcoin.outputIndex].scriptPubKey == txNew.vout[1].scriptPubKey &&
153-
pcoin.tx->GetHash() != txNew.vin[0].prevout.hash)
153+
pcoin.tx->GetHashForSpendingOutput() != txNew.vin[0].prevout.hash)
154154
{
155155
if(pcoin.tx->vout[pcoin.outputIndex].nValue + nCredit > nCombineThreshold)
156156
continue;
@@ -171,7 +171,7 @@ void PoSTransactionCreator::CombineUtxos(
171171
nCredit + pcoin.tx->vout[pcoin.outputIndex].nValue > nCombineThreshold)
172172
break;
173173

174-
txNew.vin.push_back(CTxIn(pcoin.tx->GetHash(), pcoin.outputIndex));
174+
txNew.vin.emplace_back(pcoin.tx->GetHashForSpendingOutput(), pcoin.outputIndex);
175175
nCredit += pcoin.tx->vout[pcoin.outputIndex].nValue;
176176
walletTransactions.push_back(pcoin.tx);
177177
}
@@ -340,4 +340,4 @@ bool PoSTransactionCreator::CreateProofOfStake(
340340

341341
stakedCoins_->resetTimestamp(); //this will trigger stake set to repopulate next round
342342
return true;
343-
}
343+
}

divi/src/StakableCoin.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
#ifndef STAKABLE_COIN_H
22
#define STAKABLE_COIN_H
3+
#include <merkletx.h>
34
#include <uint256.h>
4-
#include <primitives/transaction.h>
55
struct StakableCoin
66
{
7-
const CTransaction* tx;
7+
const CMerkleTx* tx;
88
unsigned outputIndex;
99
uint256 blockHashOfFirstConfirmation;
1010

@@ -16,7 +16,7 @@ struct StakableCoin
1616
}
1717

1818
StakableCoin(
19-
const CTransaction* txIn,
19+
const CMerkleTx* txIn,
2020
unsigned outputIndexIn,
2121
uint256 blockHashIn
2222
): tx(txIn)
@@ -29,4 +29,4 @@ struct StakableCoin
2929
return blockHashOfFirstConfirmation < other.blockHashOfFirstConfirmation;
3030
}
3131
};
32-
#endif//STAKABLE_COIN_H
32+
#endif//STAKABLE_COIN_H

divi/src/kernel.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ bool CheckProofOfStakeContextAndRecoverStakingData(
254254
uint256 hashBlock;
255255
CTransaction txPrev;
256256
if (!GetTransaction(txin.prevout.hash, txPrev, hashBlock, true))
257-
return error("CheckProofOfStake() : INFO: read txPrev failed");
257+
return error("CheckProofOfStake() : INFO: read txPrev failed for %s", txin.prevout.hash.GetHex());
258258

259259
const CScript &kernelScript = txPrev.vout[txin.prevout.n].scriptPubKey;
260260

divi/src/merkletx.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <primitives/block.h>
44
#include <chainparams.h>
55
#include <chain.h>
6+
#include <ForkActivation.h>
67
#include <txmempool.h>
78
#include <ValidationState.h>
89
#include <swifttx.h>
@@ -182,4 +183,23 @@ bool CMerkleTx::IsTransactionLockTimedOut() const
182183
}
183184

184185
return false;
185-
}
186+
}
187+
188+
uint256 CMerkleTx::GetHashForSpendingOutput() const
189+
{
190+
const CBlockIndex* pindexBlock = nullptr;
191+
const int depth = GetNumberOfBlockConfirmationsINTERNAL(pindexBlock);
192+
193+
/* If the transaction is not yet confirmed in a block, we use the current
194+
tip to determine the segwit-light activation status. This is not
195+
perfect around the activation time, but there is nothing we can do
196+
in that case anyway. Mempool and wallet discourage spending unconfirmed
197+
outputs around the segwit-light fork anyway. */
198+
if (depth <= 0)
199+
pindexBlock = chainActive.Tip();
200+
201+
assert(pindexBlock != nullptr);
202+
const ActivationState as(pindexBlock);
203+
204+
return as.IsActive(Fork::SegwitLight) ? GetBareTxid() : GetHash();
205+
}

divi/src/merkletx.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,11 @@ class CMerkleTx : public CTransaction
5757
bool AcceptToMemoryPool(bool fLimitFree = true, bool fRejectInsaneFee = true, bool ignoreFees = false);
5858
int GetTransactionLockSignatures() const;
5959
bool IsTransactionLockTimedOut() const;
60+
61+
/** Returns the prevout hash that should be used for spending outputs
62+
* created by this transaction. This takes the activation of segwit-light
63+
* at the time of confirmation into account. */
64+
uint256 GetHashForSpendingOutput() const;
65+
6066
};
61-
#endif// MERKLE_TX_H
67+
#endif// MERKLE_TX_H

divi/src/rpcrawtransaction.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,8 @@ Value listunspent(const Array& params, bool fHelp)
322322
"[ (array of json object)\n"
323323
" {\n"
324324
" \"txid\" : \"txid\", (string) the transaction id \n"
325+
" \"baretxid\" : \"baretxid\", (string) The bare txid (without signatures)\n"
326+
" \"outputhash\" : \"outputhash\", (string) The hash (txid or bare txid) that should be used for spending\n"
325327
" \"vout\" : n, (numeric) the vout value\n"
326328
" \"address\" : \"address\", (string) the divi address\n"
327329
" \"account\" : \"account\", (string) The associated account, or \"\" for the default account\n"
@@ -379,6 +381,8 @@ Value listunspent(const Array& params, bool fHelp)
379381
const CScript& pk = out.tx->vout[out.i].scriptPubKey;
380382
Object entry;
381383
entry.push_back(Pair("txid", out.tx->GetHash().GetHex()));
384+
entry.push_back(Pair("baretxid", out.tx->GetBareTxid().GetHex()));
385+
entry.push_back(Pair("outputhash", out.tx->GetHashForSpendingOutput().GetHex()));
382386
entry.push_back(Pair("vout", out.i));
383387
CTxDestination address;
384388
if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) {

divi/src/txmempool.cpp

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -696,12 +696,7 @@ void CTxMemPool::check(const CCoinsViewCache* pcoins) const
696696

697697
CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(pcoins));
698698

699-
/* We use an activation state based on the current tip for determining
700-
the state of segwit-light. Spending of unconfirmed outputs in the
701-
mempool is not allowed in a time window "around" the fork, so that
702-
this should be good enough. */
703699
const ActivationState as(chainActive.Tip());
704-
const bool segwitLight = as.IsActive(Fork::SegwitLight);
705700

706701
LOCK(cs);
707702
list<const CTxMemPoolEntry*> waitingOnDependants;
@@ -713,8 +708,7 @@ void CTxMemPool::check(const CCoinsViewCache* pcoins) const
713708
for (const auto& txin : tx.vin) {
714709
// Check that every mempool transaction's inputs refer to available coins, or other mempool tx's.
715710
CTransaction tx2;
716-
if ((segwitLight && lookupBareTxid(txin.prevout.hash, tx2))
717-
|| (!segwitLight && lookup(txin.prevout.hash, tx2))) {
711+
if (lookupOutpoint(txin.prevout.hash, tx2)) {
718712
assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull());
719713
fDependsWait = true;
720714
} else {
@@ -794,6 +788,18 @@ bool CTxMemPool::lookupBareTxid(const uint256& btxid, CTransaction& result) cons
794788
return true;
795789
}
796790

791+
bool CTxMemPool::lookupOutpoint(const uint256& hash, CTransaction& result) const
792+
{
793+
/* We use an activation state based on the current tip for determining
794+
the state of segwit-light. Spending of unconfirmed outputs in the
795+
mempool is not allowed in a time window "around" the fork, so that
796+
this should be good enough. */
797+
const ActivationState as(chainActive.Tip());
798+
return as.IsActive(Fork::SegwitLight)
799+
? lookupBareTxid(hash, result)
800+
: lookup(hash, result);
801+
}
802+
797803
CFeeRate CTxMemPool::estimateFee(int nBlocks) const
798804
{
799805
LOCK(cs);
@@ -873,10 +879,7 @@ bool CCoinsViewMemPool::GetCoins(const uint256& txid, CCoins& coins) const
873879
// conflict with the underlying cache, and it cannot have pruned entries (as it contains full)
874880
// transactions. First checking the underlying cache risks returning a pruned entry instead.
875881
CTransaction tx;
876-
const ActivationState as(chainActive.Tip());
877-
const bool segwitLight = as.IsActive(Fork::SegwitLight);
878-
if ((segwitLight && mempool.lookupBareTxid(txid, tx))
879-
|| (!segwitLight && mempool.lookup(txid, tx))) {
882+
if (mempool.lookupOutpoint(txid, tx)) {
880883
coins = CCoins(tx, MEMPOOL_HEIGHT);
881884
return true;
882885
}

divi/src/txmempool.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,11 @@ class CTxMemPool
192192
bool lookup(const uint256& hash, CTransaction& result) const;
193193
bool lookupBareTxid(const uint256& btxid, CTransaction& result) const;
194194

195+
/** Looks up a transaction by its outpoint for spending. This takes the
196+
* state of segwit light activation into account for deciding whether to
197+
* use the normal txid or the bare txid map. */
198+
bool lookupOutpoint(const uint256& hash, CTransaction& result) const;
199+
195200
/** Estimate fee rate needed to get into the next nBlocks */
196201
CFeeRate estimateFee(int nBlocks) const;
197202

divi/src/wallet.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ CAmount CWallet::GetDebit(const CWalletTx& tx, const isminefilter& filter) const
354354
CAmount CWallet::ComputeCredit(const CWalletTx& tx, const isminefilter& filter, int creditFilterFlags) const
355355
{
356356
CAmount nCredit = 0;
357-
uint256 hash = tx.GetHash();
357+
const uint256 hash = tx.GetHashForSpendingOutput();
358358
for (unsigned int i = 0; i < tx.vout.size(); i++) {
359359
if( (creditFilterFlags & REQUIRE_UNSPENT) && IsSpent(tx,i)) continue;
360360
if( (creditFilterFlags & REQUIRE_UNLOCKED) && IsLockedCoin(hash,i)) continue;
@@ -952,7 +952,7 @@ set<uint256> CWallet::GetConflicts(const uint256& txid) const
952952
*/
953953
bool CWallet::IsSpent(const CWalletTx& wtx, unsigned int n) const
954954
{
955-
return outputTracker_->IsSpent(wtx.GetHash(), n);
955+
return outputTracker_->IsSpent(wtx.GetHashForSpendingOutput(), n);
956956
}
957957

958958
bool CWallet::GetMasternodeVinAndKeys(CTxIn& txinRet, CPubKey& pubKeyRet, CKey& keyRet, std::string strTxHash, std::string strOutputIndex)
@@ -997,7 +997,7 @@ bool CWallet::GetVinAndKeysFromOutput(COutput out, CTxIn& txinRet, CPubKey& pubK
997997

998998
CScript pubScript;
999999

1000-
txinRet = CTxIn(out.tx->GetHash(), out.i);
1000+
txinRet = CTxIn(out.tx->GetHashForSpendingOutput(), out.i);
10011001
pubScript = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey
10021002

10031003
CTxDestination address1;
@@ -1723,7 +1723,7 @@ bool CWallet::IsAvailableForSpending(const CWalletTx* pcoin, unsigned int i, con
17231723
}
17241724
}
17251725

1726-
const uint256 hash = pcoin->GetHash();
1726+
const uint256 hash = pcoin->GetHashForSpendingOutput();
17271727

17281728
if (IsSpent(*pcoin, i))
17291729
return false;
@@ -2242,8 +2242,8 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
22422242
}
22432243

22442244
// Fill vin
2245-
BOOST_FOREACH (const PAIRTYPE(const CWalletTx*, unsigned int) & coin, setCoins)
2246-
txNew.vin.push_back(CTxIn(coin.first->GetHash(), coin.second));
2245+
for (const auto& coin : setCoins)
2246+
txNew.vin.emplace_back(coin.first->GetHashForSpendingOutput(), coin.second);
22472247

22482248
// Sign
22492249
int nIn = 0;

0 commit comments

Comments
 (0)