Skip to content

Commit 5d7a873

Browse files
committed
Fix "Spent Stake" vulnerability
1 parent e11e4fb commit 5d7a873

26 files changed

+86
-5
lines changed

configure.ac

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N)
22
AC_PREREQ([2.60])
33
define(_CLIENT_VERSION_MAJOR, 2)
4-
define(_CLIENT_VERSION_MINOR, 1)
4+
define(_CLIENT_VERSION_MINOR, 2)
55
define(_CLIENT_VERSION_REVISION, 0)
66
define(_CLIENT_VERSION_BUILD, 0)
77
define(_CLIENT_VERSION_IS_RELEASE, true)
8-
define(_COPYRIGHT_YEAR, 2017)
8+
define(_COPYRIGHT_YEAR, 2019)
99
AC_INIT([TittieCoin Core],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_REVISION],[www.tittiecoin.com],[tittiecoin])
1010
AC_CONFIG_SRCDIR([src/main.cpp])
1111
AC_CONFIG_HEADERS([src/config/tittiecoin-config.h])

src/clientversion.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
//! These need to be macros, as clientversion.cpp's and tittiecoin*-res.rc's voodoo requires it
1717
#define CLIENT_VERSION_MAJOR 2
18-
#define CLIENT_VERSION_MINOR 1
18+
#define CLIENT_VERSION_MINOR 2
1919
#define CLIENT_VERSION_REVISION 0
2020
#define CLIENT_VERSION_BUILD 0
2121

src/main.cpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ CCriticalSection cs_main;
5353

5454
BlockMap mapBlockIndex;
5555
map<uint256, uint256> mapProofOfStake;
56+
map<COutPoint, int> mapStakeSpent;
5657
set<pair<COutPoint, unsigned int> > setStakeSeen;
5758
map<unsigned int, unsigned int> mapHashedBlocks;
5859
CChain chainActive;
@@ -2004,6 +2005,8 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
20042005
if (coins->vout.size() < out.n + 1)
20052006
coins->vout.resize(out.n + 1);
20062007
coins->vout[out.n] = undo.txout;
2008+
// erase the spent input
2009+
mapStakeSpent.erase(out);
20072010
}
20082011
}
20092012
}
@@ -2235,6 +2238,33 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
22352238
if (!pblocktree->WriteTxIndex(vPos))
22362239
return state.Abort("Failed to write transaction index");
22372240

2241+
2242+
2243+
2244+
// add new entries
2245+
for (const CTransaction tx: block.vtx) {
2246+
if (tx.IsCoinBase())
2247+
continue;
2248+
for (const CTxIn in: tx.vin) {
2249+
LogPrint("map", "mapStakeSpent: Insert %s | %u\n", in.prevout.ToString(), pindex->nHeight);
2250+
mapStakeSpent.insert(std::make_pair(in.prevout, pindex->nHeight));
2251+
}
2252+
}
2253+
2254+
2255+
// delete old entries
2256+
for (auto it = mapStakeSpent.begin(); it != mapStakeSpent.end();) {
2257+
if (it->second < pindex->nHeight - Params().MaxReorganizationDepth()) {
2258+
LogPrint("map", "mapStakeSpent: Erase %s | %u\n", it->first.ToString(), it->second);
2259+
it = mapStakeSpent.erase(it);
2260+
}
2261+
else {
2262+
it++;
2263+
}
2264+
}
2265+
2266+
2267+
22382268
// add this block to the view's block chain
22392269
view.SetBestBlock(pindex->GetBlockHash());
22402270

@@ -3338,6 +3368,57 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
33383368

33393369
int nHeight = pindex->nHeight;
33403370

3371+
3372+
if (block.IsProofOfStake()) {
3373+
LOCK(cs_main);
3374+
3375+
CCoinsViewCache coins(pcoinsTip);
3376+
3377+
if (!coins.HaveInputs(block.vtx[1])) {
3378+
// the inputs are spent at the chain tip so we should look at the recently spent outputs
3379+
3380+
for (CTxIn in : block.vtx[1].vin) {
3381+
auto it = mapStakeSpent.find(in.prevout);
3382+
if (it == mapStakeSpent.end()) {
3383+
return false;
3384+
}
3385+
if (it->second <= pindexPrev->nHeight) {
3386+
return false;
3387+
}
3388+
}
3389+
}
3390+
3391+
// if this is on a fork
3392+
if (!chainActive.Contains(pindexPrev) && pindexPrev != NULL) {
3393+
// start at the block we're adding on to
3394+
CBlockIndex *last = pindexPrev;
3395+
3396+
// while that block is not on the main chain
3397+
while (!chainActive.Contains(last) && pindexPrev != NULL) {
3398+
CBlock bl;
3399+
ReadBlockFromDisk(bl, last);
3400+
// loop through every spent input from said block
3401+
for (CTransaction t : bl.vtx) {
3402+
for (CTxIn in: t.vin) {
3403+
// loop through every spent input in the staking transaction of the new block
3404+
for (CTxIn stakeIn : block.vtx[1].vin) {
3405+
// if they spend the same input
3406+
if (stakeIn.prevout == in.prevout) {
3407+
// reject the block
3408+
return false;
3409+
}
3410+
}
3411+
}
3412+
}
3413+
3414+
3415+
// go to the parent block
3416+
last = pindexPrev->pprev;
3417+
}
3418+
}
3419+
}
3420+
3421+
33413422
// Write block to history file
33423423
try {
33433424
unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);

src/qt/res/icons/bitcoin.icns

139 KB
Binary file not shown.

src/qt/res/icons/history.png

-24.2 KB
Loading

src/qt/res/icons/masternodes.png

27.7 KB
Loading

src/qt/res/icons/overview.png

-25.1 KB
Loading

src/qt/res/icons/receive.png

-20.4 KB
Loading

src/qt/res/icons/send.png

-21.4 KB
Loading

src/qt/res/icons/tx_mined.png

7.89 KB
Loading

0 commit comments

Comments
 (0)