Skip to content

Commit

Permalink
Fix "Spent Stake" vulnerability
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex committed Jan 25, 2019
1 parent a31ee18 commit 4139b71
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 14 deletions.
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ AC_PREREQ([2.60])
define(_CLIENT_VERSION_MAJOR, 1)
define(_CLIENT_VERSION_MINOR, 0)
define(_CLIENT_VERSION_REVISION, 0)
define(_CLIENT_VERSION_BUILD, 0)
define(_CLIENT_VERSION_BUILD, 1)
define(_CLIENT_VERSION_IS_RELEASE, true)
define(_COPYRIGHT_YEAR, 2018)
AC_INIT([BirakeCoin Core],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_REVISION],[www.birake.com],[birakecoin])
Expand Down
4 changes: 2 additions & 2 deletions contrib/gitian-descriptors/gitian-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ packages:
- "bsdmainutils"
- "ca-certificates"
- "python"
reference_datetime: "2015-06-01 00:00:00"
reference_datetime: "2019-01-25 00:00:00"
remotes:
- "url": "git@git.webflow.ro:intranet/birakecoin.git"
- "url": "git@github.com:birake/birakecoin.git"
"dir": "birakecoin"
files: []
script: |
Expand Down
7 changes: 4 additions & 3 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,12 @@ static void convertSeed6(std::vector<CAddress>& vSeedsOut, const SeedSpec6* data
// + Contains no strange transactions
static Checkpoints::MapCheckpoints mapCheckpoints =
boost::assign::map_list_of
(0, uint256("00000c89a16d5d75011aed4ed5e21efc7bf3ffebe7411f46632771c0ceb907c3"));
(0, uint256("00000c89a16d5d75011aed4ed5e21efc7bf3ffebe7411f46632771c0ceb907c3"))
(228961, uint256("834881adde0d162c7238fb2537bfc7dbfae9421512d03fd87ca4cf3d645e596f"));
static const Checkpoints::CCheckpointData data = {
&mapCheckpoints,
1533728621, // * UNIX timestamp of last checkpoint block
0, // * total number of transactions between genesis and last checkpoint
1548429411, // * UNIX timestamp of last checkpoint block
556302, // * total number of transactions between genesis and last checkpoint
// (the tx=... number in the SetBestChain debug.log lines)
2000 // * estimated number of transactions per day after checkpoint
};
Expand Down
2 changes: 1 addition & 1 deletion src/clientversion.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
#define CLIENT_VERSION_MAJOR 1
#define CLIENT_VERSION_MINOR 0
#define CLIENT_VERSION_REVISION 0
#define CLIENT_VERSION_BUILD 0
#define CLIENT_VERSION_BUILD 1

//! Set to true for release, false for prerelease or test build
#define CLIENT_VERSION_IS_RELEASE true
Expand Down
82 changes: 78 additions & 4 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ BlockMap mapBlockIndex;
map<uint256, uint256> mapProofOfStake;
set<pair<COutPoint, unsigned int> > setStakeSeen;
map<unsigned int, unsigned int> mapHashedBlocks;
map<COutPoint, int> mapStakeSpent;
CChain chainActive;
CBlockIndex* pindexBestHeader = NULL;
int64_t nTimeBestReceived = 0;
Expand Down Expand Up @@ -2309,6 +2310,8 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
if (coins->vout.size() < out.n + 1)
coins->vout.resize(out.n + 1);
coins->vout[out.n] = undo.txout;
// erase the spent input
mapStakeSpent.erase(out);
}
}
}
Expand Down Expand Up @@ -2542,6 +2545,28 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
if (!pblocktree->WriteTxIndex(vPos))
return state.Abort("Failed to write transaction index");

// add new entries
for (const CTransaction tx: block.vtx) {
if (tx.IsCoinBase())
continue;
for (const CTxIn in: tx.vin) {
LogPrint("map", "mapStakeSpent: Insert %s | %u\n", in.prevout.ToString(), pindex->nHeight);
mapStakeSpent.insert(std::make_pair(in.prevout, pindex->nHeight));
}
}


// delete old entries
for (auto it = mapStakeSpent.begin(); it != mapStakeSpent.end();) {
if (it->second < pindex->nHeight - Params().MaxReorganizationDepth()) {
LogPrint("map", "mapStakeSpent: Erase %s | %u\n", it->first.ToString(), it->second);
it = mapStakeSpent.erase(it);
}
else {
it++;
}
}

// add this block to the view's block chain
view.SetBestBlock(pindex->GetBlockHash());

Expand Down Expand Up @@ -3677,6 +3702,55 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,

int nHeight = pindex->nHeight;

if (block.IsProofOfStake()) {
LOCK(cs_main);

CCoinsViewCache coins(pcoinsTip);

if (!coins.HaveInputs(block.vtx[1])) {
// the inputs are spent at the chain tip so we should look at the recently spent outputs

for (CTxIn in : block.vtx[1].vin) {
auto it = mapStakeSpent.find(in.prevout);
if (it == mapStakeSpent.end()) {
return false;
}
if (it->second <= pindexPrev->nHeight) {
return false;
}
}
}

// if this is on a fork
if (!chainActive.Contains(pindexPrev) && pindexPrev != NULL) {
// start at the block we're adding on to
CBlockIndex *last = pindexPrev;

// while that block is not on the main chain
while (!chainActive.Contains(last) && pindexPrev != NULL) {
CBlock bl;
ReadBlockFromDisk(bl, last);
// loop through every spent input from said block
for (CTransaction t : bl.vtx) {
for (CTxIn in: t.vin) {
// loop through every spent input in the staking transaction of the new block
for (CTxIn stakeIn : block.vtx[1].vin) {
// if they spend the same input
if (stakeIn.prevout == in.prevout) {
// reject the block
return false;
}
}
}
}


// go to the parent block
last = pindexPrev->pprev;
}
}
}

// Write block to history file
try {
unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
Expand Down Expand Up @@ -5683,23 +5757,23 @@ int ActiveProtocol()

// SPORK_14 was used for 70710. Leave it 'ON' so they don't see < 70710 nodes. They won't react to SPORK_15
// messages because it's not in their code
/*

if (IsSporkActive(SPORK_14_NEW_PROTOCOL_ENFORCEMENT)) {
if (chainActive.Tip()->nHeight >= Params().ModifierUpgradeBlock())
return MIN_PEER_PROTO_VERSION_AFTER_ENFORCEMENT;
}

return MIN_PEER_PROTO_VERSION_BEFORE_ENFORCEMENT;
*/



// SPORK_15 is used for 70910. Nodes < 70910 don't see it and still get their protocol version via SPORK_14 and their
// own ModifierUpgradeBlock()
/*
if (IsSporkActive(SPORK_15_NEW_PROTOCOL_ENFORCEMENT_2))
return MIN_PEER_PROTO_VERSION_AFTER_ENFORCEMENT;
return MIN_PEER_PROTO_VERSION_BEFORE_ENFORCEMENT;
*/
}

// requires LOCK(cs_vRecvMsg)
Expand Down
6 changes: 3 additions & 3 deletions src/version.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* network protocol versioning
*/

static const int PROTOCOL_VERSION = 70810;
static const int PROTOCOL_VERSION = 70811;

//! initial proto version, to be increased after version/verack negotiation
static const int INIT_PROTO_VERSION = 209;
Expand All @@ -21,8 +21,8 @@ static const int INIT_PROTO_VERSION = 209;
static const int GETHEADERS_VERSION = 70077;

//! disconnect from peers older than this proto version
static const int MIN_PEER_PROTO_VERSION_BEFORE_ENFORCEMENT = 70710;
static const int MIN_PEER_PROTO_VERSION_AFTER_ENFORCEMENT = 70810;
static const int MIN_PEER_PROTO_VERSION_BEFORE_ENFORCEMENT = 70810;
static const int MIN_PEER_PROTO_VERSION_AFTER_ENFORCEMENT = 70811;

//! nTime field added to CAddress, starting with this version;
//! if possible, avoid requesting addresses nodes older than this
Expand Down

0 comments on commit 4139b71

Please sign in to comment.