Skip to content

Commit

Permalink
Merge pull request Peershares#76 from sigmike/peercoin_functional_tests
Browse files Browse the repository at this point in the history
Added functional tests
  • Loading branch information
sigmike committed Oct 18, 2014
2 parents 6658331 + 65cc046 commit 4536df1
Show file tree
Hide file tree
Showing 30 changed files with 1,097 additions and 0 deletions.
190 changes: 190 additions & 0 deletions src/bitcoinrpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,187 @@ Value stop(const Array& params, bool fHelp)



#ifdef TESTING

Value generatestake(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 1)
throw runtime_error(
"generatestake [<parent block hash>]\n"
"generate a single proof of stake block on top of <parent block hash> (default: highest block hash)"
);

if (GetBoolArg("-stakegen", true))
throw JSONRPCError(-3, "Stake generation enabled. Won't start another generation.");

CBlockIndex *parent;
if (params.size() > 1)
{
uint256 parentHash;
parentHash.SetHex(params[1].get_str());
if (!mapBlockIndex.count(parentHash))
throw JSONRPCError(-3, "Parent hash not in main chain");
parent = mapBlockIndex[parentHash];
}
else
{
parent = pindexBest;
}

BitcoinMiner(pwalletMain, true, true, parent);
return hashSingleStakeBlock.ToString();
}


extern bool fRequestShutdown;
Value shutdown(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 0)
throw runtime_error(
"shutdown\n"
"close the program"
);

fRequestShutdown = true;

return "";
}


Value timetravel(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error(
"timetravel <seconds>\n"
"change relative time"
);

nTimeShift += params[0].get_int();

return "";
}


unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake);

Value duplicateblock(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error(
"duplicateblock <original hash> [<parent hash>]\n"
"propagate a new block with the same stake as block <original hash> on top of <parent hash> (default: same parent)"
);

uint256 originalHash;
originalHash.SetHex(params[0].get_str());
if (!mapBlockIndex.count(originalHash))
throw JSONRPCError(-3, "Original hash not in main chain");
CBlockIndex *original = mapBlockIndex[originalHash];

CBlockIndex *parent;
if (params.size() > 1)
{
uint256 parentHash;
parentHash.SetHex(params[1].get_str());
if (!mapBlockIndex.count(parentHash))
throw JSONRPCError(-3, "Parent hash not in main chain");
parent = mapBlockIndex[parentHash];
}
else
{
parent = original->pprev;
}

CWallet *pwallet = pwalletMain;
CReserveKey reservekey(pwalletMain);
bool fProofOfStake = true;
unsigned int nExtraNonce = 0;

auto_ptr<CBlock> pblock(new CBlock());
if (!pblock.get())
throw JSONRPCError(-3, "Unable to allocate block");

// Create coinbase tx
CTransaction txNew;
txNew.vin.resize(1);
txNew.vin[0].prevout.SetNull();
txNew.vout.resize(1);
CPubKey reservepubkey;
if (!reservekey.GetReservedKey(reservepubkey))
throw JSONRPCError(-3, "Unable to get reserve pub key");
txNew.vout[0].scriptPubKey << reservepubkey << OP_CHECKSIG;

// Add our coinbase tx as first transaction
pblock->vtx.push_back(txNew);

// ppcoin: if coinstake available add coinstake tx
CBlockIndex* pindexPrev = parent;
IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce);

CBlock originalBlock;
originalBlock.ReadFromDisk(original);
CTransaction txCoinStake(originalBlock.vtx[1]);

pblock->vtx[0].vout[0].SetEmpty();
pblock->vtx[0].nTime = txCoinStake.nTime;
pblock->vtx.push_back(txCoinStake);

pblock->nBits = GetNextTargetRequired(pindexPrev, pblock->IsProofOfStake());

// Fill in header
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
if (pblock->IsProofOfStake())
pblock->nTime = pblock->vtx[1].nTime; //same as coinstake timestamp
pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, pblock->GetMaxTransactionTime());
pblock->nTime = max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - nMaxClockDrift);
if (pblock->IsProofOfWork())
pblock->UpdateTime(pindexPrev);
pblock->nNonce = 0;

if (fProofOfStake)
{
// ppcoin: if proof-of-stake block found then process block
if (pblock->IsProofOfStake())
{
if (!pblock->SignBlock(*pwallet))
{
// We ignore errors to be able to test duplicate blocks with invalid signature
}

// Found a solution
{
LOCK(cs_main);

// Remove key from key pool
reservekey.KeepKey();

// Track how many getdata requests this block gets
{
LOCK(pwallet->cs_wallet);
pwallet->mapRequestCount[pblock->GetHash()] = 0;
}

// Process this block the same as if we had received it from another node
// But do not check for errors as this is expected to fail
CValidationState state;
ProcessBlock(state, NULL, pblock.get());
}
}
else
throw JSONRPCError(-3, "generated block is not a Proof of Stake");
}

string result(pblock->GetHash().ToString());

pblock.release();

return result;
}

#endif


//
// Call Table
//
Expand Down Expand Up @@ -361,6 +542,12 @@ static const CRPCCommand vRPCCommands[] =
{ "gettxout", &gettxout, true, false },
{ "lockunspent", &lockunspent, false, false },
{ "listlockunspent", &listlockunspent, false, false },
#ifdef TESTING
{ "generatestake", &generatestake, true, false },
{ "duplicateblock", &duplicateblock, true, false },
{ "shutdown", &shutdown, true, false },
{ "timetravel", &timetravel, true, false },
#endif
};

CRPCTable::CRPCTable()
Expand Down Expand Up @@ -1295,6 +1482,9 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri
if (strMethod == "lockunspent" && n > 1) ConvertTo<Array>(params[1]);
if (strMethod == "importprivkey" && n > 2) ConvertTo<bool>(params[2]);

#ifdef TESTING
if (strMethod == "timetravel" && n > 0) ConvertTo<boost::int64_t>(params[0]);
#endif
return params;
}

Expand Down
8 changes: 8 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1076,6 +1076,11 @@ bool AppInit2(boost::thread_group& threadGroup)
printf("mapWallet.size() = %"PRIszu"\n", pwalletMain->mapWallet.size());
printf("mapAddressBook.size() = %"PRIszu"\n", pwalletMain->mapAddressBook.size());

#ifdef TESTING
if (mapArgs.count("-timetravel"))
nTimeShift = GetArg("-timetravel", 0);
#endif

StartNode(threadGroup);

if (fServer)
Expand All @@ -1085,6 +1090,9 @@ bool AppInit2(boost::thread_group& threadGroup)
GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain);

// ppcoin: mint proof-of-stake blocks in the background
#ifdef TESTING
if (GetBoolArg("-stakegen", true))
#endif
MintStake(threadGroup, pwalletMain);

// ********************************************************* Step 12: finished
Expand Down
56 changes: 56 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ const string strMessageMagic = "PPCoin Signed Message:\n";
double dHashesPerSec = 0.0;
int64 nHPSTimerStart = 0;

#ifdef TESTING
uint256 hashSingleStakeBlock;
#endif

// Settings
int64 nTransactionFee = MIN_TX_FEE;

Expand Down Expand Up @@ -1171,7 +1175,11 @@ const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfSta
return pindex;
}

#ifdef TESTING
unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake)
#else
unsigned int static GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake)
#endif
{
if (pindexLast == NULL)
return bnProofOfWorkLimit.GetCompact(); // genesis block
Expand Down Expand Up @@ -3107,12 +3115,21 @@ bool LoadBlockIndex()

if (fTestNet)
{
#ifdef TESTING
hashGenesisBlock = uint256("00008d0d88095d31f6dbdbcf80f6e51f71adf2be15740301f5e05cc0f3b2d2c0");
bnProofOfWorkLimit = CBigNum(~uint256(0) >> 15);
nStakeMinAge = 60 * 60 * 24; // test net min age is 1 day
nCoinbaseMaturity = 60;
bnInitialHashTarget = CBigNum(~uint256(0) >> 15);
nModifierInterval = 60 * 20; // test net modifier interval is 20 minutes
#else
hashGenesisBlock = hashGenesisBlockTestNet;
bnProofOfWorkLimit = CBigNum(~uint256(0) >> 28);
nStakeMinAge = 60 * 60 * 24; // test net min age is 1 day
nCoinbaseMaturity = 60;
bnInitialHashTarget = CBigNum(~uint256(0) >> 29);
nModifierInterval = 60 * 20; // test net modifier interval is 20 minutes
#endif
}

printf("%s Network: genesis=0x%s nBitsLimit=0x%08x nBitsInitial=0x%08x nStakeMinAge=%d nCoinbaseMaturity=%d nModifierInterval=%d\n",
Expand Down Expand Up @@ -3170,6 +3187,18 @@ bool InitBlockIndex() {
block.nNonce = 122894938;
}

#ifdef TESTING
CBigNum bnTarget;
bnTarget.SetCompact(block.nBits);
while (block.GetHash() > bnTarget.getuint256())
{
if (block.nNonce % 1048576 == 0)
printf("n=%dM hash=%s\n", block.nNonce / 1048576,
block.GetHash().ToString().c_str());
block.nNonce++;
}
#endif

//// debug print
uint256 hash = block.GetHash();
printf("%s\n", hash.ToString().c_str());
Expand Down Expand Up @@ -5059,7 +5088,11 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey)
return true;
}

#ifdef TESTING
void BitcoinMiner(CWallet *pwallet, bool fProofOfStake, bool fGenerateSingleBlock, CBlockIndex *parent)
#else
void BitcoinMiner(CWallet *pwallet, bool fProofOfStake)
#endif
{
printf("CPUMiner started for proof-of-%s\n", fProofOfStake? "stake" : "work");
SetThreadPriority(THREAD_PRIORITY_LOWEST);
Expand All @@ -5086,7 +5119,15 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake)
// Create new block
//
unsigned int nTransactionsUpdatedLast = nTransactionsUpdated;
#ifdef TESTING
CBlockIndex* pindexPrev;
if (parent)
pindexPrev = parent;
else
pindexPrev = pindexBest;
#else
CBlockIndex* pindexPrev = pindexBest;
#endif

auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlock(reservekey, pwallet, fProofOfStake));
if (!pblocktemplate.get())
Expand All @@ -5106,9 +5147,20 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake)
}
strMintWarning = "";
printf("CPUMiner : proof-of-stake block found %s\n", pblock->GetHash().ToString().c_str());
#ifdef TESTING
SetThreadPriority(THREAD_PRIORITY_NORMAL);
bool fSuccess = CheckWork(pblock, *pwalletMain, reservekey);
SetThreadPriority(THREAD_PRIORITY_LOWEST);
if (fSuccess && fGenerateSingleBlock)
{
hashSingleStakeBlock = pblock->GetHash();
return;
}
#else
SetThreadPriority(THREAD_PRIORITY_NORMAL);
CheckWork(pblock, *pwalletMain, reservekey);
SetThreadPriority(THREAD_PRIORITY_LOWEST);
#endif
}
MilliSleep(500);
continue;
Expand Down Expand Up @@ -5246,7 +5298,11 @@ void GenerateBitcoins(bool fGenerate, CWallet* pwallet)

minerThreads = new boost::thread_group();
for (int i = 0; i < nThreads; i++)
#ifdef TESTING
minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet, false, false, (CBlockIndex*)NULL));
#else
minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet, false));
#endif
}

// ppcoin: stake minter thread
Expand Down
7 changes: 7 additions & 0 deletions src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ extern bool fBenchmark;
extern int nScriptCheckThreads;
extern bool fTxIndex;
extern unsigned int nCoinCacheSize;
#ifdef TESTING
extern uint256 hashSingleStakeBlock;
#endif

// Settings
extern int64 nTransactionFee;
Expand Down Expand Up @@ -196,7 +199,11 @@ bool IsInitialBlockDownload();
std::string GetWarnings(std::string strFor);
uint256 WantedByOrphan(const CBlock* pblockOrphan);
const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake);
#ifdef TESTING
void BitcoinMiner(CWallet *pwallet, bool fProofOfStake, bool fGenerateSingleBlock = false, CBlockIndex* parent = NULL);
#else
void BitcoinMiner(CWallet *pwallet, bool fProofOfStake);
#endif
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false);
/** Connect/disconnect blocks until pindexNew is the new tip of the active block chain */
Expand Down
3 changes: 3 additions & 0 deletions src/rpcwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ Value getinfo(const Array& params, bool fHelp)
if (pwalletMain->IsCrypted())
obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime / 1000));
obj.push_back(Pair("errors", GetWarnings("statusbar")));
#ifdef TESTING
obj.push_back(Pair("time", DateTimeStrFormat(GetAdjustedTime())));
#endif
return obj;
}

Expand Down
7 changes: 7 additions & 0 deletions src/test/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
source 'https://rubygems.org'

gem 'docker-api', require: 'docker'
gem 'httparty'

gem 'cucumber'
gem 'rspec-expectations'

0 comments on commit 4536df1

Please sign in to comment.