Skip to content
This repository has been archived by the owner on Aug 7, 2019. It is now read-only.

Commit

Permalink
Security updates, version and protocol bump
Browse files Browse the repository at this point in the history
  • Loading branch information
livenodescoin committed Mar 17, 2019
1 parent 2b20ae3 commit 45206a3
Show file tree
Hide file tree
Showing 11 changed files with 198 additions and 72 deletions.
6 changes: 3 additions & 3 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N)
AC_PREREQ([2.60])
define(_CLIENT_VERSION_MAJOR, 1)
define(_CLIENT_VERSION_MINOR, 0)
define(_CLIENT_VERSION_REVISION, 0)
define(_CLIENT_VERSION_REVISION, 1)
define(_CLIENT_VERSION_BUILD, 0)
define(_CLIENT_VERSION_IS_RELEASE, true)
define(_COPYRIGHT_YEAR, 2018)
AC_INIT([LivenodesCoin Core],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_REVISION],[www.livenodes.net],[livenodes])
define(_COPYRIGHT_YEAR, 2019)
AC_INIT([LivenodesCoin Core],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_REVISION],[livenodes.online],[livenodes])
AC_CONFIG_SRCDIR([src/main.cpp])
AC_CONFIG_HEADERS([src/config/livenodes-config.h])
AC_CONFIG_AUX_DIR([build-aux])
Expand Down
8 changes: 4 additions & 4 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,14 @@ 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("000005e5c00c68a4216e7aa53b2896177590cba3e98030b25434d3fc0d244267"))
;
(27685, uint256("6780ee7c3fb0accb36697ad4dbad7570b55a6784b249c6f4a54a67d169f7fb9b"));

static const Checkpoints::CCheckpointData data = {
&mapCheckpoints,
1550966400, // * UNIX timestamp of last checkpoint block
0, // * total number of transactions between genesis and last checkpoint
1552755963, // * UNIX timestamp of last checkpoint block
26487, // * 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
1440 // * estimated number of transactions per day after checkpoint
};

static Checkpoints::MapCheckpoints mapCheckpointsTestnet =
Expand Down
2 changes: 1 addition & 1 deletion src/clientversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const std::string CLIENT_NAME("LivenodesCoin Core");
"v" DO_STRINGIZE(maj) "." DO_STRINGIZE(min) "." DO_STRINGIZE(rev) "." DO_STRINGIZE(build) "-g" commit

#define BUILD_DESC_FROM_UNKNOWN(maj, min, rev, build) \
"v" DO_STRINGIZE(maj) "." DO_STRINGIZE(min) "." DO_STRINGIZE(rev) "." DO_STRINGIZE(build) "-unk"
"v" DO_STRINGIZE(maj) "." DO_STRINGIZE(min) "." DO_STRINGIZE(rev) "." DO_STRINGIZE(build) ""

#ifndef BUILD_DESC
#ifdef BUILD_SUFFIX
Expand Down
2 changes: 1 addition & 1 deletion src/clientversion.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
//! These need to be macros, as clientversion.cpp's and livenodes*-res.rc's voodoo requires it
#define CLIENT_VERSION_MAJOR 1
#define CLIENT_VERSION_MINOR 0
#define CLIENT_VERSION_REVISION 0
#define CLIENT_VERSION_REVISION 1
#define CLIENT_VERSION_BUILD 0

//! Set to true for release, false for prerelease or test build
Expand Down
4 changes: 4 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,10 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-rpcsslprivatekeyfile=<file.pem>", strprintf(_("Server private key (default: %s)"), "server.pem"));
strUsage += HelpMessageOpt("-rpcsslciphers=<ciphers>", strprintf(_("Acceptable ciphers (default: %s)"), "TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH"));

strUsage += HelpMessageOpt("-blockspamfilter=<n>", strprintf(_("Use block spam filter (default: %u)"), DEFAULT_BLOCK_SPAM_FILTER));
strUsage += HelpMessageOpt("-blockspamfiltermaxsize=<n>", strprintf(_("Maximum size of the list of indexes in the block spam filter (default: %u)"), DEFAULT_BLOCK_SPAM_FILTER_MAX_SIZE));
strUsage += HelpMessageOpt("-blockspamfiltermaxavg=<n>", strprintf(_("Maximum average size of an index occurrence in the block spam filter (default: %u)"), DEFAULT_BLOCK_SPAM_FILTER_MAX_AVG));

return strUsage;
}

Expand Down
203 changes: 164 additions & 39 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,8 @@ CCriticalSection cs_main;
BlockMap mapBlockIndex;
map<uint256, uint256> mapProofOfStake;
set<pair<COutPoint, unsigned int> > setStakeSeen;
// Merge pull request #133 from phoreproject/fix-block-spam
map<COutPoint, int> mapStakeSpent;
map<unsigned int, unsigned int> mapHashedBlocks;
map<COutPoint, int> mapStakeSpent;
CChain chainActive;
CBlockIndex* pindexBestHeader = NULL;
int64_t nTimeBestReceived = 0;
Expand Down Expand Up @@ -256,6 +255,80 @@ struct CBlockReject {
uint256 hashBlock;
};

class CNodeBlocks
{
public:
CNodeBlocks():
maxSize(0),
maxAvg(0)
{
maxSize = GetArg("-blockspamfiltermaxsize", DEFAULT_BLOCK_SPAM_FILTER_MAX_SIZE);
maxAvg = GetArg("-blockspamfiltermaxavg", DEFAULT_BLOCK_SPAM_FILTER_MAX_AVG);
}

bool onBlockReceived(int nHeight) {
if(nHeight > 0 && maxSize && maxAvg) {
addPoint(nHeight);
return true;
}
return false;
}

bool updateState(CValidationState& state, bool ret)
{
// No Blocks
size_t size = points.size();
if(size == 0)
return ret;

// Compute the number of the received blocks
size_t nBlocks = 0;
for(auto point : points)
{
nBlocks += point.second;
}

// Compute the average value per height
double nAvgValue = (double)nBlocks / size;

// Ban the node if try to spam
bool banNode = (nAvgValue >= 1.5 * maxAvg && size >= maxAvg) ||
(nAvgValue >= maxAvg && nBlocks >= maxSize) ||
(nBlocks >= maxSize * 3);
if(banNode)
{
// Clear the points and ban the node
points.clear();
return state.DoS(100, error("block-spam ban node for sending spam"));
}

return ret;
}

private:
void addPoint(int height)
{
// Remove the last element in the list
if(points.size() == maxSize)
{
points.erase(points.begin());
}

// Add the point to the list
int occurrence = 0;
auto mi = points.find(height);
if (mi != points.end())
occurrence = (*mi).second;
occurrence++;
points[height] = occurrence;
}

private:
std::map<int,int> points;
size_t maxSize;
size_t maxAvg;
};

/**
* Maintain validation-specific state about nodes, protected by cs_main, instead
* by CNode's own locks. This simplifies asynchronous operation, where
Expand Down Expand Up @@ -290,6 +363,8 @@ struct CNodeState {
//! Whether we consider this a preferred download peer.
bool fPreferredDownload;

CNodeBlocks nodeBlocks;

CNodeState()
{
fCurrentlyConnected = false;
Expand Down Expand Up @@ -2248,15 +2323,14 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
if (tx.IsCoinBase())
continue;
for (const CTxIn in: tx.vin) {
LogPrint("map", "mapStakeSpent: Insert %s | %u\n", in.prevout.ToString(), pindex->nHeight);
// 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);
// LogPrint("map", "mapStakeSpent: Erase %s | %u\n", it->first.ToString(), it->second);
it = mapStakeSpent.erase(it);
}
else {
Expand Down Expand Up @@ -3422,6 +3496,15 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
if (block.GetHash() != Params().HashGenesisBlock() && !CheckWork(block, pindexPrev))
return false;

bool isPoS = false;
if (block.IsProofOfStake()) {
isPoS = true;
uint256 hashProofOfStake = 0;
uint256 hash = block.GetHash();
if(!mapProofOfStake.count(hash)) // add to mapProofOfStake
mapProofOfStake.insert(make_pair(hash, hashProofOfStake));
}

if (!AcceptBlockHeader(block, state, &pindex))
return false;

Expand All @@ -3441,51 +3524,45 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,

int nHeight = pindex->nHeight;

if (block.IsProofOfStake()) {
if (isPoS) {
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;
// Check whether is a fork or not
if (pindexPrev != nullptr && !chainActive.Contains(pindexPrev)) {

// Start at the block we're adding on to
CBlockIndex *prev = pindexPrev;
CTransaction &stakeTxIn = block.vtx[1];
int readBlock = 0;
CBlock bl;
// Go backwards on the forked chain up to the split
do {
if(readBlock == Params().MaxReorganizationDepth()){
// TODO: Remove this chain from disk.
return error("%s: forked chain longer than maximum reorg limit", __func__);
}
}
}
if(!ReadBlockFromDisk(bl, prev))
// Previous block not on disk
return error("%s: previous block %s not on disk", __func__, prev->GetBlockHash().GetHex());

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

// while that block is not on the main chain
while (!chainActive.Contains(last) && last != NULL) {
CBlock bl;
ReadBlockFromDisk(bl, last);
// loop through every spent input from said block
// Loop through every 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
// Loop through every input of the staking tx
for (CTxIn stakeIn : stakeTxIn.vin) {
// if it's already spent
if (stakeIn.prevout == in.prevout) {
// reject the block
return false;
return state.DoS(100, error("%s: input already spent on a previous block", __func__));
}
}
}
}
prev = prev->pprev;

// go to the parent block
last = last->pprev;
}
} while (!chainActive.Contains(prev));
}
}

Expand Down Expand Up @@ -3603,15 +3680,64 @@ bool ProcessNewBlock(CValidationState& state, CNode* pfrom, CBlock* pblock, CDis
return error("%s : CheckBlock FAILED for block %s", __func__, pblock->GetHash().GetHex());
}

if (pblock->IsProofOfStake()) {
//LogPrintf("%s : check stake inputs\n", __func__);
CCoinsViewCache coins(pcoinsTip);
// check stake inputs in current coinsView and reject block if inputs are not allowed
if (!coins.HaveInputs(pblock->vtx[1])) {
std::pair<COutPoint, unsigned int> ProofOfStake = pblock->GetProofOfStake();

// the inputs are spent at the chain tip so we should look at the recently spent outputs
auto it = mapStakeSpent.find(ProofOfStake.first/*pblock->vtx[1].vin[0].prevout*/);
if (it == mapStakeSpent.end())
return state.DoS(100, error("%s : stake input missing/spent", __func__));

// Check for coin age.
// First try finding the previous transaction in database.
CTransaction txPrev;
uint256 hashBlockPrev;
if (!GetTransaction(ProofOfStake.first.hash/*pblock->vtx[1].vin[0].prevout.hash*/, txPrev, hashBlockPrev, true))
return state.DoS(100, error("%s : stake failed to find vin transaction", __func__));
// Find block in map.
CBlockIndex* pindex = NULL;
BlockMap::iterator itBlock = mapBlockIndex.find(hashBlockPrev);
if (itBlock != mapBlockIndex.end())
pindex = itBlock->second;
else
return state.DoS(100, error("%s : stake failed to find block index", __func__));
// Check block time vs stake age requirement.
if (pindex->GetBlockHeader().nTime + nStakeMinAge > ProofOfStake.second/*pblock->GetBlockHeader().nTime*/)
return state.DoS(100, error("%s : stake under min. stake age", __func__));

}
}

// Store to disk
CBlockIndex* pindex = NULL;
bool ret = AcceptBlock(*pblock, state, &pindex, dbp, checked);
if (pindex && pfrom) {
mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId();
}
CheckBlockIndex();
if (!ret)
if (!ret){
// Check spamming
if(pfrom && GetBoolArg("-blockspamfilter", DEFAULT_BLOCK_SPAM_FILTER)) {
CNodeState *nodestate = State(pfrom->GetId());
nodestate->nodeBlocks.onBlockReceived(pindex->nHeight);
bool nodeStatus = true;
// UpdateState will return false if the node is attacking us or update the score and return true.
nodeStatus = nodestate->nodeBlocks.updateState(state, nodeStatus);
int nDoS = 0;
if (state.IsInvalid(nDoS)) {
if (nDoS > 0)
Misbehaving(pfrom->GetId(), nDoS);
nodeStatus = false;
}
if(!nodeStatus)
return error("%s : AcceptBlock FAILED - block spam protection", __func__);
}
return error("%s : AcceptBlock FAILED", __func__);
}
}

if (!ActivateBestChain(state, pblock, checked))
Expand Down Expand Up @@ -5431,8 +5557,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,

int ActiveProtocol()
{
// if (IsSporkActive(SPORK_X_NEW_PROTOCOL_ENFORCEMENT_X))
// return MIN_PEER_PROTO_VERSION_AFTER_ENFORCEMENT;
if (IsSporkActive(SPORK_8_NEW_PROTOCOL_ENFORCEMENT)) return MIN_PEER_PROTO_VERSION_AFTER_ENFORCEMENT;

return MIN_PEER_PROTO_VERSION_BEFORE_ENFORCEMENT;
}
Expand Down
9 changes: 8 additions & 1 deletion src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ static const int MAX_SCRIPTCHECK_THREADS = 16;
/** -par default (number of script-checking threads, 0 = auto) */
static const int DEFAULT_SCRIPTCHECK_THREADS = 0;
/** Number of blocks that can be requested at any given time from a single peer. */
static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 16;
static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 256;
/** Timeout in seconds during which a peer must stall block download progress before being disconnected. */
static const unsigned int BLOCK_STALLING_TIMEOUT = 2;
/** Number of headers sent in one getheaders result. We rely on the assumption that if a peer sends
Expand All @@ -103,6 +103,13 @@ static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111;
/** Enable bloom filter */
static const bool DEFAULT_PEERBLOOMFILTERS = true;

/** Default for -blockspamfilter, use header spam filter */
static const bool DEFAULT_BLOCK_SPAM_FILTER = true;
/** Default for -blockspamfiltermaxsize, maximum size of the list of indexes in the block spam filter */
static const unsigned int DEFAULT_BLOCK_SPAM_FILTER_MAX_SIZE = COINBASE_MATURITY;
/** Default for -blockspamfiltermaxavg, maximum average size of an index occurrence in the block spam filter */
static const unsigned int DEFAULT_BLOCK_SPAM_FILTER_MAX_AVG = 10;

/** "reject" message codes */
static const unsigned char REJECT_MALFORMED = 0x01;
static const unsigned char REJECT_INVALID = 0x10;
Expand Down
5 changes: 3 additions & 2 deletions src/qt/bitcoingui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ BitcoinGUI::~BitcoinGUI()
if (trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu)
trayIcon->hide();
#ifdef Q_OS_MAC
delete appMenuBar;
//delete appMenuBar;
MacDockIconHandler::cleanup();
#endif
}
Expand Down Expand Up @@ -513,7 +513,8 @@ void BitcoinGUI::createMenuBar()
{
#ifdef Q_OS_MAC
// Create a decoupled menu bar on Mac which stays even if the window is closed
appMenuBar = new QMenuBar();
//appMenuBar = new QMenuBar();
appMenuBar = menuBar();
#else
// Get the main window's menu bar on other platforms
appMenuBar = menuBar();
Expand Down
Loading

0 comments on commit 45206a3

Please sign in to comment.