Merge pull request #60 from airk42/cb-inputs

rebased cb-inputs merge back into master
This commit is contained in:
airk42 2018-02-06 21:53:46 -05:00 committed by GitHub
commit 20d7e7a08e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 616 additions and 67 deletions

View File

@ -82,6 +82,13 @@ AC_ARG_ENABLE([wallet],
[enable_wallet=$enableval],
[enable_wallet=yes])
# Enable fork mining
AC_ARG_ENABLE([fork],
[AS_HELP_STRING([--enable-fork],
[enable fork (default is yes)])],
[enable_fork=$enableval],
[enable_fork=yes])
AC_ARG_ENABLE([mining],
[AS_HELP_STRING([--enable-mining],
[enable mining (default is yes)])],
@ -856,6 +863,16 @@ else
AC_MSG_RESULT(no)
fi
dnl enable fork_mining
AC_MSG_CHECKING([if fork mining should be enabled])
if test x$enable_fork != xno; then
AC_MSG_RESULT(yes)
AC_DEFINE(FORK_CB_INPUT, 1, [Define to 1 to enable fork mining functions])
else
AC_MSG_RESULT(no)
fi
dnl enable mining
AC_MSG_CHECKING([if mining should be enabled])
if test x$enable_mining != xno; then
@ -931,6 +948,7 @@ AM_CONDITIONAL([TARGET_DARWIN], [test x$TARGET_OS = xdarwin])
AM_CONDITIONAL([BUILD_DARWIN], [test x$BUILD_OS = xdarwin])
AM_CONDITIONAL([TARGET_WINDOWS], [test x$TARGET_OS = xwindows])
AM_CONDITIONAL([ENABLE_WALLET],[test x$enable_wallet = xyes])
AM_CONDITIONAL([FORK_CB_INPUT],[test x$enable_fork = xyes])
AM_CONDITIONAL([ENABLE_MINING],[test x$enable_mining = xyes])
AM_CONDITIONAL([ENABLE_RUST],[test x$enable_rust = xyes])
AM_CONDITIONAL([ENABLE_TESTS],[test x$BUILD_TEST = xyes])

View File

@ -165,7 +165,7 @@ Threads
- ThreadRPCServer : Remote procedure call handler, listens on port 7932 for connections and services them.
- ZcashMiner : Generates zcash (if wallet is enabled).
- BTCPrivate Miner : Generates BTCPrivate (if wallet is enabled).
- Shutdown : Does an orderly shutdown of everything.

View File

@ -492,6 +492,11 @@ std::string HelpMessage(HelpMessageMode mode)
if (GetBoolArg("-help-debug", false))
strUsage += HelpMessageOpt("-blockversion=<n>", strprintf("Override block version to test forking scenarios (default: %d)", (int)CBlock::CURRENT_VERSION));
#ifdef FORK_CB_INPUT
strUsage += HelpMessageGroup(_("Fork :"));
strUsage += HelpMessageOpt("-utxo-path=<path>", _("Specify location of utxo files"));
#endif
#ifdef ENABLE_MINING
strUsage += HelpMessageGroup(_("Mining options:"));
strUsage += HelpMessageOpt("-gen", strprintf(_("Generate coins (default: %u)"), 0));
@ -891,6 +896,13 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
#endif
}
#ifdef FORK_CB_INPUT
forkUtxoPath = GetArg("-utxo-path", "");
forkStartHeight = GetArg("-fork-startheight", FORK_BLOCK_HEIGHT_START);
forkHeightRange = GetArg("-fork-heightrange", FORK_BLOCK_HEIGHT_RANGE);
forkCBPerBlock = GetArg("-fork-cbperblock", FORK_COINBASE_PER_BLOCK);
#endif
// ********************************************************* Step 3: parameter-to-internal-flags
fDebug = !mapMultiArgs["-debug"].empty();

View File

@ -71,6 +71,33 @@ size_t nCoinCacheUsage = 5000 * 300;
uint64_t nPruneTarget = 0;
bool fAlerts = DEFAULT_ALERTS;
#ifdef FORK_CB_INPUT
#include <boost/format.hpp>
#include <boost/range/combine.hpp>
std::string forkUtxoPath;
int64_t forkStartHeight = FORK_BLOCK_HEIGHT_START;
int64_t forkHeightRange = FORK_BLOCK_HEIGHT_RANGE;
int64_t forkCBPerBlock = FORK_COINBASE_PER_BLOCK;
std::string GetUTXOFileName(int nHeight)
{
boost::filesystem::path utxo_path(forkUtxoPath);
if (utxo_path.empty() || !utxo_path.has_filename())
{
LogPrintf("GetUTXOFileName(): UTXO path is not specified, add utxo-path=<path-to-utxop-files> to your btcprivate.conf and restart");
return "";
}
std::stringstream ss;
ss << boost::format("utxo-%05i.bin") % (nHeight - forkStartHeight);
boost::filesystem::path utxo_file = utxo_path;
utxo_file /= ss.str();
return utxo_file.generic_string();
}
#endif
/** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */
CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
@ -1354,7 +1381,11 @@ bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::M
return true;
}
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos)
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos
#ifdef FORK_CB_INPUT
, int nHeight
#endif
)
{
block.SetNull();
@ -1371,17 +1402,30 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos)
return error("%s: Deserialize or I/O error - %s at %s", __func__, e.what(), pos.ToString());
}
#ifdef FORK_CB_INPUT
if (!isForkBlock(nHeight)) { //when block is in fork region - don't check Solution and PoW
#endif
// Check the header
if (!(CheckEquihashSolution(&block, Params()) &&
CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())))
return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString());
#ifdef FORK_CB_INPUT
}
#endif
return true;
}
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex)
{
if (!ReadBlockFromDisk(block, pindex->GetBlockPos()))
if (!ReadBlockFromDisk(block, pindex->GetBlockPos()
#ifdef FORK_CB_INPUT
, pindex->nHeight
#endif
)
)
return false;
if (block.GetHash() != pindex->GetBlockHash())
return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s",
@ -1887,20 +1931,25 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
const CTransaction &tx = block.vtx[i];
uint256 hash = tx.GetHash();
int nNonCBIdx = 0;
// restore inputs
// Check that all outputs are available and match the outputs in the block itself
// exactly.
{
CCoinsModifier outs = view.ModifyCoins(hash);
outs->ClearUnspendable();
{
CCoinsModifier outs = view.ModifyCoins(hash);
outs->ClearUnspendable();
CCoins outsBlock(tx, pindex->nHeight);
// The CCoins serialization does not serialize negative numbers.
// No network rules currently depend on the version here, so an inconsistency is harmless
// but it must be corrected before txout nversion ever influences a network rule.
if (outsBlock.nVersion < 0)
outs->nVersion = outsBlock.nVersion;
if (*outs != outsBlock)
fClean = fClean && error("DisconnectBlock(): added transaction mismatch? database corrupted");
CCoins outsBlock(tx, pindex->nHeight);
// The CCoins serialization does not serialize negative numbers.
// No network rules currently depend on the version here, so an inconsistency is harmless
// but it must be corrected before txout nversion ever influences a network rule.
if (outsBlock.nVersion < 0)
outs->nVersion = outsBlock.nVersion;
if (*outs != outsBlock) {
fClean = fClean && error("DisconnectBlock(): added transaction mismatch? database corrupted");
LogPrintf("Transaction mismatch?: id: %d: Amount: %d; ScriptPubKey: %s\n", i, tx.vout[0].nValue, tx.vout[0].scriptPubKey.ToString());
}
// remove outputs
outs->Clear();
@ -1913,8 +1962,12 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
}
}
// restore inputs
if (i > 0) { // not coinbases
#ifdef FORK_CB_INPUT
if (isForkBlock(pindex->nHeight)){ //when block in forking region - all transcations are coinbase
nNonCBIdx = forkCBPerBlock;
}
#endif
if (i > nNonCBIdx) { // not coinbases
const CTxUndo &txundo = blockUndo.vtxundo[i-1];
if (txundo.vprevout.size() != tx.vin.size())
return error("DisconnectBlock(): transaction and undo data inconsistent");
@ -2188,12 +2241,18 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
int64_t nTime1 = GetTimeMicros(); nTimeConnect += nTime1 - nTimeStart;
LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime1 - nTimeStart), 0.001 * (nTime1 - nTimeStart) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime1 - nTimeStart) / (nInputs-1), nTimeConnect * 0.000001);
#ifdef FORK_CB_INPUT
if (!isForkBlock(pindex->nHeight)){ //when block is in forking region - don't check coinbase amount
#endif
CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus());
if (block.vtx[0].GetValueOut() > blockReward)
return state.DoS(100,
error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)",
block.vtx[0].GetValueOut(), blockReward),
REJECT_INVALID, "bad-cb-amount");
#ifdef FORK_CB_INPUT
}
#endif
if (!control.Wait())
return state.DoS(100, false);
@ -2970,6 +3029,10 @@ bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool f
return state.DoS(100, error("CheckBlockHeader(): block version too low"),
REJECT_INVALID, "version-too-low");
#ifdef FORK_CB_INPUT
if (!isTipInForkRange()) { //when in FORK mode (tip is in forking region) - don't check Solution and PoW
#endif
// Check Equihash solution is valid
if (fCheckPOW && !CheckEquihashSolution(&block, Params()))
return state.DoS(100, error("CheckBlockHeader(): Equihash solution invalid"),
@ -2980,6 +3043,10 @@ bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool f
return state.DoS(50, error("CheckBlockHeader(): proof of work failed"),
REJECT_INVALID, "high-hash");
#ifdef FORK_CB_INPUT
}
#endif
// Check timestamp
if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60)
return state.Invalid(error("CheckBlockHeader(): block timestamp too far in the future"),
@ -3028,10 +3095,21 @@ bool CheckBlock(const CBlock& block, CValidationState& state,
if (block.vtx.empty() || !block.vtx[0].IsCoinBase())
return state.DoS(100, error("CheckBlock(): first tx is not coinbase"),
REJECT_INVALID, "bad-cb-missing");
for (unsigned int i = 1; i < block.vtx.size(); i++)
if (block.vtx[i].IsCoinBase())
return state.DoS(100, error("CheckBlock(): more than one coinbase"),
REJECT_INVALID, "bad-cb-multiple");
#ifdef FORK_CB_INPUT
if (isTipInForkRange()) { //when in FORK mode (tip is in forking region) blocks might have up to fork pre-defined value coinbases
if (block.vtx.size() > forkCBPerBlock)
return state.DoS(100, error("CheckBlock(): it is forking block but there are more than %d coinbase txns", forkCBPerBlock),
REJECT_INVALID, "bad-cb-multiple");
} else {
#endif
for (unsigned int i = 1; i < block.vtx.size(); i++)
if (block.vtx[i].IsCoinBase())
return state.DoS(100, error("CheckBlock(): more than one coinbase"),
REJECT_INVALID, "bad-cb-multiple");
#ifdef FORK_CB_INPUT
}
#endif
// Check transactions
BOOST_FOREACH(const CTransaction& tx, block.vtx)
@ -3063,9 +3141,15 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta
int nHeight = pindexPrev->nHeight+1;
// Check proof of work
#ifdef FORK_CB_INPUT
if (!isForkBlock(nHeight)) { //If current block is FORK, don't check work required
#endif
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams))
return state.DoS(100, error("%s: incorrect proof of work", __func__),
REJECT_INVALID, "bad-diffbits");
#ifdef FORK_CB_INPUT
}
#endif
// Check timestamp against prev
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast())
@ -3164,7 +3248,11 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc
return true;
}
bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, bool fRequested, CDiskBlockPos* dbp)
bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, bool fRequested, CDiskBlockPos* dbp
#ifdef FORK_CB_INPUT
, bool fCalledFromMiner
#endif
)
{
const CChainParams& chainparams = Params();
AssertLockHeld(cs_main);
@ -3207,6 +3295,97 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
int nHeight = pindex->nHeight;
#ifdef FORK_CB_INPUT
if (isForkBlock(nHeight)) { //if block is in forking region validate it agains file records
if (!fCalledFromMiner && !forkUtxoPath.empty()) {
std::string utxo_file_path = GetUTXOFileName(nHeight);
std::ifstream if_utxo(utxo_file_path, std::ios::binary | std::ios::in);
if (!if_utxo.is_open()) {
LogPrintf("AcceptBlock(): FORK Block - Cannot open UTXO file - %s\n", utxo_file_path);
} else {
LogPrintf("AcceptBlock(): FORK Block - Validating block - %u with UTXO file - %s\n", nHeight, utxo_file_path);
vector<pair<uint64_t, CScript> > txFromFile;
txFromFile.reserve(forkCBPerBlock);
int recs = 0;
while (if_utxo && recs < forkCBPerBlock) {
char term = 0;
char coin[8] = {};
if (!if_utxo.read(coin, 8)) {
LogPrintf("AcceptBlock(): FORK Block - No more data in the file \n");
break;
}
uint64_t amount = bytes2uint64(coin);
char pubkeysize[8] = {};
if (!if_utxo.read(pubkeysize, 8)) {
LogPrintf("AcceptBlock(): FORK Block - UTXO file corrupted? - Not more data (PubKeyScript size)\n");
break;
}
int pbsize = bytes2uint64(pubkeysize);
if (pbsize == 0) {
LogPrintf("AcceptBlock(): FORK Block - UTXO file corrupted? - Warning! PubKeyScript size = 0\n");
//but proceed
}
std::unique_ptr<char[]> pubKeyScript(new char[pbsize]);
if (!if_utxo.read(&pubKeyScript[0], pbsize)) {
LogPrintf("AcceptBlock(): FORK Block - UTXO file corrupted? - Not more data (PubKeyScript)\n");
break;
}
unsigned char* pks = (unsigned char*)pubKeyScript.get();
CScript script = CScript(pks, pks+pbsize);
txFromFile.push_back(make_pair(amount, script));
if (!if_utxo.read(&term, 1)) {
LogPrintf("AcceptBlock(): FORK Block - UTXO file corrupted? - No more data (record separator)\n");
break;
}
if (term != '\n') {
//This maybe not an error, but warning none the less
LogPrintf("AcceptBlock(): FORK Block - UTXO file corrupted? - Warning! No record separator ('0xA') was found\n");
if_utxo.seekg(-1, ios_base::cur); //move one char back - if it is not a separator, maybe there is not separators at all
}
recs++;
}
LogPrintf("AcceptBlock(): FORK Block - %d records read from UTXO file\n", recs);
if (txFromFile.size() != block.vtx.size() ||
recs != block.vtx.size()){
state.DoS(100, error("AcceptBlock(): Number of file records - %d doesn't match number of transcations in block - %d\n", recs, block.vtx.size()),
REJECT_INVALID, "bad-fork-block");
pindex->nStatus |= BLOCK_FAILED_VALID;
setDirtyBlockIndex.insert(pindex);
return false;
}
int txid = 0;
typedef boost::tuple<pair<uint64_t, CScript>&, const CTransaction&> fork_cmp_tuple;
BOOST_FOREACH(fork_cmp_tuple cmp, boost::combine(txFromFile, block.vtx)) {
pair<uint64_t, CScript>& rec = cmp.get<0>();
const CTransaction& tx = cmp.get<1>();
if (rec.first != tx.vout[0].nValue ||
rec.second != tx.vout[0].scriptPubKey)
{
LogPrintf("AcceptBlock(): FORK Block - Error: Transaction (%d) mismatch\n", txid);
LogPrintf("AcceptBlock(): Transaction: Amount: %d; scriptPubKey: %s\n", tx.vout[0].nValue, tx.vout[0].scriptPubKey.ToString());
LogPrintf("AcceptBlock(): File Record: Amount: %d; scriptPubKey: %s\n", rec.first, rec.second.ToString());
state.DoS(100, error("AcceptBlock(): FORK Block - Transaction (%d) doesn't match record in the UTXO file", txid),
REJECT_INVALID, "bad-fork-block");
pindex->nStatus |= BLOCK_FAILED_VALID;
setDirtyBlockIndex.insert(pindex);
return false;
}
}
txid++;
}
}
}
#endif
// Write block to history file
try {
unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
@ -3243,7 +3422,11 @@ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned
}
bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp)
bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp
#ifdef FORK_CB_INPUT
, bool fCalledFromMiner
#endif
)
{
// Preliminary checks
auto verifier = libzcash::ProofVerifier::Disabled();
@ -3259,7 +3442,11 @@ bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, bool
// Store to disk
CBlockIndex *pindex = NULL;
bool ret = AcceptBlock(*pblock, state, &pindex, fRequested, dbp);
bool ret = AcceptBlock(*pblock, state, &pindex, fRequested, dbp
#ifdef FORK_CB_INPUT
, fCalledFromMiner
#endif
);
if (pindex && pfrom) {
mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId();
}
@ -3289,6 +3476,7 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex
// NOTE: CheckBlockHeader is called by CheckBlock
if (!ContextualCheckBlockHeader(block, state, pindexPrev))
return false;
if (!CheckBlock(block, state, verifier, fCheckPOW, fCheckMerkleRoot))
return false;
if (!ContextualCheckBlock(block, state, pindexPrev))
@ -3475,7 +3663,12 @@ CBlockIndex * InsertBlockIndex(uint256 hash)
bool static LoadBlockIndexDB()
{
const CChainParams& chainparams = Params();
#ifdef FORK_CB_INPUT
if (!pblocktree->LoadBlockIndexGuts(forkStartHeight, forkStartHeight+forkHeightRange))
#else
if (!pblocktree->LoadBlockIndexGuts())
#endif
return false;
boost::this_thread::interruption_point();

View File

@ -174,7 +174,12 @@ void UnregisterNodeSignals(CNodeSignals& nodeSignals);
* @param[out] dbp If pblock is stored to disk (or already there), this will be set to its location.
* @return True if state.IsValid()
*/
bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp);
bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp
#ifdef FORK_CB_INPUT
, bool fCalledFromMiner = false
#endif
);
/** Check whether enough disk space is available for an incoming block */
bool CheckDiskSpace(uint64_t nAdditionalBytes = 0);
/** Open a block file (blk?????.dat) */
@ -396,7 +401,11 @@ public:
/** Functions for disk access for blocks */
bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart);
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos);
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos
#ifdef FORK_CB_INPUT
, int nHeight = -1
#endif
);
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex);
@ -413,6 +422,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
/** Context-independent validity checks */
bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true);
bool CheckBlock(const CBlock& block, CValidationState& state,
libzcash::ProofVerifier& verifier,
bool fCheckPOW = true, bool fCheckMerkleRoot = true);
@ -431,7 +441,11 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex
* - The only caller of AcceptBlock verifies JoinSplit proofs elsewhere.
* If dbp is non-NULL, the file is known to already reside on disk
*/
bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex **pindex, bool fRequested, CDiskBlockPos* dbp);
bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex **pindex, bool fRequested, CDiskBlockPos* dbp
#ifdef FORK_CB_INPUT
, bool fCalledFromMiner = false
#endif
);
bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex **ppindex= NULL);
@ -527,4 +541,72 @@ namespace Consensus {
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, const Consensus::Params& consensusParams);
}
#ifdef FORK_CB_INPUT
#define FORK_BLOCK_HEIGHT_START 1000000 //current ZCL height is 200K-300K, this value here is placeholder, it will have to be changed to correct fork block height
#define FORK_BLOCK_HEIGHT_RANGE 65000
#define FORK_COINBASE_PER_BLOCK 10000
extern std::string forkUtxoPath;
extern int64_t forkStartHeight;
extern int64_t forkHeightRange;
extern int64_t forkCBPerBlock;
std::string GetUTXOFileName(int nHeight);
//ex: forkStartHeight = 300 000; forkHeightRange = 65K
//A. for miner:
// 1. Current height = 299 999; the next block to create 300 000
// nHeight is 300 000 - return false
// 2. Current height = 300 000; the next block to create 300 001
// nHeight is 300 001 - return true - file to use utxo-00001.bin
// ...
//
// n-1. Current height = 364 999; the next block to create 365 000
// nHeight is 365 000 - return true - file to use utxo-65000.bin
// n. Current height = 365 000; the next block to create 365 001
// nHeight is 365 001 - return false
//
// fork blocks 300001 - 365000
//
//B. for acceptblock:
// 1. Current height = 299 999
// nHeight is 299 999 - return false - no verification
// 2. Current height = 300 000
// nHeight is 300 000 - return false - no verification
// 3. Current height = 300 001
// nHeight is 300 001 - return true - verify with file utxo-00001.bin
// ...
//
// n. Current height = 365 000
// nHeight is 365 000 - return true - verify with file utxo-65000.bin
// n+1. Current height = 365 001
// nHeight is 365 001 - return false - no verification
//
inline bool isForkBlock(int nHeight)
{
return (nHeight > forkStartHeight && nHeight <= forkStartHeight + forkHeightRange);
}
inline bool isTipInForkRange()
{
return chainActive.Tip()? isForkBlock(chainActive.Tip()->nHeight): false;
}
inline bool isNextTipInForkRange()
{
return chainActive.Tip()? isForkBlock(chainActive.Tip()->nHeight + 1): false;
}
inline uint64_t bytes2uint64(char *array)
{
uint64_t x =
static_cast<uint64_t>(array[0]) & 0x00000000000000ff |
static_cast<uint64_t>(array[1]) << 8 & 0x000000000000ff00 |
static_cast<uint64_t>(array[2]) << 16 & 0x0000000000ff0000 |
static_cast<uint64_t>(array[3]) << 24 & 0x00000000ff000000 |
static_cast<uint64_t>(array[4]) << 32 & 0x000000ff00000000 |
static_cast<uint64_t>(array[5]) << 40 & 0x0000ff0000000000 |
static_cast<uint64_t>(array[6]) << 48 & 0x00ff000000000000 |
static_cast<uint64_t>(array[7]) << 56 & 0xff00000000000000;
return x;
}
#endif
#endif // BITCOIN_MAIN_H

View File

@ -40,6 +40,11 @@
#endif
#include <mutex>
#ifdef FORK_CB_INPUT
#include <fstream>
#endif
using namespace std;
//////////////////////////////////////////////////////////////////////////////
@ -106,6 +111,143 @@ void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams,
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams);
}
#ifdef FORK_CB_INPUT
CBlockTemplate* CreateNewForkBlock(bool& bFileNotFound)
{
CBlockIndex* pindexPrev = chainActive.Tip();
const int nHeight = pindexPrev->nHeight + 1;
string utxo_file_path = GetUTXOFileName(nHeight);
std::ifstream if_utxo(utxo_file_path, std::ios::binary | std::ios::in);
if (!if_utxo.is_open()) {
bFileNotFound = true;
LogPrintf("CreateNewForkBlock(): Cannot open UTXO file - %s\n", utxo_file_path);
return NULL;
}
const CChainParams& chainparams = Params();
// Create new block
std::unique_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate());
if(!pblocktemplate.get())
return NULL;
CBlock *pblock = &pblocktemplate->block; // pointer for convenience
// -regtest only: allow overriding block.nVersion with
// -blockversion=N to test forking scenarios
if (Params().MineBlocksOnDemand())
pblock->nVersion = GetArg("-blockversion", pblock->nVersion);
// Largest block you're willing to create:
unsigned int nBlockMaxSize = (unsigned int)(MAX_BLOCK_SIZE-1000);
// Minimum block size you want to create; block will be filled with free transactions
// until there are no more or the block reaches this size:
unsigned int nBlockMinSize = DEFAULT_BLOCK_MIN_SIZE;
uint64_t nBlockSize = 1000;
uint64_t nBlockTx = 0;
while (if_utxo && nBlockTx < forkCBPerBlock) {
char term = 0;
char coin[8] = {};
if (!if_utxo.read(coin, 8)) {
LogPrintf("CreateNewForkBlock(): UTXO file corrupted? - No more data (Amount)\n");
break;
}
char pubkeysize[8] = {};
if (!if_utxo.read(pubkeysize, 8)) {
LogPrintf("CreateNewForkBlock(): UTXO file corrupted? - Not more data (PubKeyScript size)\n");
break;
}
int pbsize = bytes2uint64(pubkeysize);
//LogPrintf("CreateNewForkBlock():PubKeyScript size = %d\n", pbsize);
if (pbsize == 0) {
LogPrintf("CreateNewForkBlock(): Warning! UTXO file corrupted? PubKeyScript size = 0\n");
//but proceed
}
std::unique_ptr<char[]> pubKeyScript(new char[pbsize]);
if (!if_utxo.read(&pubKeyScript[0], pbsize)) {
LogPrintf("CreateNewForkBlock(): UTXO file corrupted? Not more data (PubKeyScript)\n");
break;
}
uint64_t amount = bytes2uint64(coin);
//LogPrintf("CreateNewForkBlock(): txn %u has ammount %u\n", nBlockTx, amount);
// Add coinbase tx's
CMutableTransaction txNew;
txNew.vin.resize(1);
txNew.vin[0].prevout.SetNull();
txNew.vout.resize(1);
unsigned char* pks = (unsigned char*)pubKeyScript.get();
txNew.vout[0].scriptPubKey = CScript(pks, pks+pbsize);
//LogPrintf("CreateNewForkBlock(): ScriptPubKey: %s\n", txNew.vout[0].scriptPubKey.ToString());
txNew.vout[0].nValue = amount;
txNew.vin[0].scriptSig = CScript() << nHeight+nBlockTx << OP_0;
unsigned int nTxSize = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION);
if (nBlockSize + nTxSize >= nBlockMaxSize)
break;//We cannot skip transaction here as in regular case - or we will loose that skipped transaction
pblock->vtx.push_back(txNew);
pblocktemplate->vTxFees.push_back(-1); // updated at end
pblocktemplate->vTxSigOps.push_back(-1); // updated at end
nBlockSize += nTxSize;
++nBlockTx;
if (!if_utxo.read(&term, 1)) {
LogPrintf("CreateNewForkBlock(): No more data (record separator)\n");
break;
}
if (term != '\n') {
//This maybe not an error, but warning none the less
LogPrintf("CreateNewForkBlock(): Warning! No record separator ('0xA') was found\n");
if_utxo.seekg(-1, ios_base::cur); //move one char back - if it is not a separator, maybe there is not separators at all
}
}
LogPrintf("CreateNewForkBlock(): %u tnxs and total size %u\n", nBlockTx, nBlockSize);
// Randomise nonce
arith_uint256 nonce = UintToArith256(GetRandHash());
// Clear the top and bottom 16 bits (for local use as thread flags and counters)
nonce <<= 32;
nonce >>= 16;
pblock->nNonce = ArithToUint256(nonce);
// Fill in header
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
pblock->hashReserved = uint256();
UpdateTime(pblock, Params().GetConsensus(), pindexPrev);
pblock->nBits = 0x207fffff; // Difficulty = 1
//0x207fffff -> 0x 7fffff00 00000000 00000000 00000000 00000000 00000000 00000000 00000000
//alternativly
//0x1d00ffff -> 0x 00000000 ffff0000 00000000 00000000 00000000 00000000 00000000 00000000
// or
//0x1f07ffff -> 0x 007fffff 00000000 00000000 00000000 00000000 00000000 00000000 00000000
pblock->nSolution.clear();
pblocktemplate->vTxSigOps[0] = 0; //NO SigOps
CValidationState state;
if (!TestBlockValidity(state, *pblock, pindexPrev, false, false))
throw std::runtime_error("CreateNewForkBlock(): TestBlockValidity failed");
return pblocktemplate.release();
}
#endif
CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
{
const CChainParams& chainparams = Params();
@ -451,14 +593,20 @@ static bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& rese
static bool ProcessBlockFound(CBlock* pblock)
#endif // ENABLE_WALLET
{
#ifdef FORK_CB_INPUT
if (!isNextTipInForkRange()) {
#endif
LogPrintf("%s\n", pblock->ToString());
#ifdef FORK_CB_INPUT
}
#endif
LogPrintf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue));
// Found a solution
{
LOCK(cs_main);
if (pblock->hashPrevBlock != chainActive.Tip()->GetBlockHash())
return error("ZcashMiner: generated block is stale");
return error("BTCPrivate Miner: generated block is stale");
}
#ifdef ENABLE_WALLET
@ -476,8 +624,12 @@ static bool ProcessBlockFound(CBlock* pblock)
// Process this block the same as if we had received it from another node
CValidationState state;
if (!ProcessNewBlock(state, NULL, pblock, true, NULL))
return error("ZcashMiner: ProcessNewBlock, block not accepted");
if (!ProcessNewBlock(state, NULL, pblock, true, NULL
#ifdef FORK_CB_INPUT
, true
#endif
))
return error("BTCPrivate Miner: ProcessNewBlock, block not accepted");
TrackMinedBlock(pblock->GetHash());
@ -490,9 +642,9 @@ void static BitcoinMiner(CWallet *pwallet)
void static BitcoinMiner()
#endif
{
LogPrintf("ZcashMiner started\n");
LogPrintf("BTCPrivate Miner started\n");
SetThreadPriority(THREAD_PRIORITY_LOWEST);
RenameThread("zcash-miner");
RenameThread("btcp-miner");
const CChainParams& chainparams = Params();
#ifdef ENABLE_WALLET
@ -520,6 +672,10 @@ void static BitcoinMiner()
);
miningTimer.start();
#ifdef FORK_CB_INPUT
bool bForkModeStarted = false;
#endif
try {
while (true) {
if (chainparams.MiningRequiresPeers()) {
@ -539,32 +695,77 @@ void static BitcoinMiner()
miningTimer.start();
}
CBlockIndex* pindexPrev = chainActive.Tip();
CBlock *pblock = nullptr;
unsigned int nTransactionsUpdatedLast = 0;
//
// Create new block
//
unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
CBlockIndex* pindexPrev = chainActive.Tip();
unique_ptr<CBlockTemplate> pblocktemplate;
#ifdef FORK_CB_INPUT
bool isNextBlockFork = isForkBlock(pindexPrev->nHeight+1);
if (isNextBlockFork) {
if (!bForkModeStarted) {
LogPrintf("BTCPrivate Miner: switching into fork mode\n");
bForkModeStarted = true;
}
bool bFileNotFound = false;
pblocktemplate.reset(CreateNewForkBlock(bFileNotFound));
if (!pblocktemplate.get()) {
if (bFileNotFound) {
MilliSleep(1000);
continue;
} else {
LogPrintf("Error in BTCPrivate Miner: Cannot create Fork Block\n");
return;
}
}
pblock = &pblocktemplate->block;
n = 48; k = 5;
LogPrintf("Running BTCPrivate Miner with %u forking transactions in block (%u bytes) and N = %d, K = %d\n", pblock->vtx.size(),
::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION),
n, k);
} else {
//if not in forking mode and/or provided file is read to the end - exit
if (bForkModeStarted) {
LogPrintf("BTCPrivate Miner: Fork is done - switching back to regular miner\n");
n = chainparams.EquihashN();
k = chainparams.EquihashK();
bForkModeStarted = false;
}
#endif
nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
#ifdef ENABLE_WALLET
unique_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey));
pblocktemplate.reset(CreateNewBlockWithKey(reservekey));
#else
unique_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey());
pblocktemplate.reset(CreateNewBlockWithKey());
#endif
if (!pblocktemplate.get())
{
if (GetArg("-mineraddress", "").empty()) {
LogPrintf("Error in ZcashMiner: Keypool ran out, please call keypoolrefill before restarting the mining thread\n");
} else {
// Should never reach here, because -mineraddress validity is checked in init.cpp
LogPrintf("Error in ZcashMiner: Invalid -mineraddress\n");
if (!pblocktemplate.get())
{
if (GetArg("-mineraddress", "").empty()) {
LogPrintf("Error in BTCPrivate Miner: Keypool ran out, please call keypoolrefill before restarting the mining thread\n");
} else {
// Should never reach here, because -mineraddress validity is checked in init.cpp
LogPrintf("Error in BTCPrivate Miner: Invalid -mineraddress\n");
}
return;
}
return;
}
CBlock *pblock = &pblocktemplate->block;
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
pblock = &pblocktemplate->block;
LogPrintf("Running ZcashMiner with %u transactions in block (%u bytes)\n", pblock->vtx.size(),
::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION));
LogPrintf("Running BTCPrivate Miner with %u transactions in block (%u bytes)\n", pblock->vtx.size(),
::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION));
#ifdef FORK_CB_INPUT
} //else
#endif
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
//
// Search
@ -597,24 +798,32 @@ void static BitcoinMiner()
solver, pblock->nNonce.ToString());
std::function<bool(std::vector<unsigned char>)> validBlock =
[&pblock, &hashTarget, &m_cs, &cancelSolver, &chainparams
#ifdef ENABLE_WALLET
[&pblock, &hashTarget, &pwallet, &reservekey, &m_cs, &cancelSolver, &chainparams]
#else
[&pblock, &hashTarget, &m_cs, &cancelSolver, &chainparams]
, &pwallet, &reservekey
#endif
(std::vector<unsigned char> soln) {
#ifdef FORK_CB_INPUT
, &isNextBlockFork
#endif
] (std::vector<unsigned char> soln) {
// Write the solution to the hash and compute the result.
LogPrint("pow", "- Checking solution against target\n");
pblock->nSolution = soln;
solutionTargetChecks.increment();
#ifdef FORK_CB_INPUT
if (!isNextBlockFork) {
#endif
if (UintToArith256(pblock->GetHash()) > hashTarget) {
return false;
}
#ifdef FORK_CB_INPUT
}
#endif
// Found a solution
SetThreadPriority(THREAD_PRIORITY_NORMAL);
LogPrintf("ZcashMiner:\n");
LogPrintf("BTCPrivate Miner:\n");
LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", pblock->GetHash().GetHex(), hashTarget.GetHex());
#ifdef ENABLE_WALLET
if (ProcessBlockFound(pblock, *pwallet, reservekey)) {
@ -691,24 +900,39 @@ void static BitcoinMiner()
// Check for stop or if block needs to be rebuilt
boost::this_thread::interruption_point();
// Regtest mode doesn't require peers
if (vNodes.empty() && chainparams.MiningRequiresPeers())
break;
if ((UintToArith256(pblock->nNonce) & 0xffff) == 0xffff)
break;
if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)
break;
#ifdef FORK_CB_INPUT
if (!isNextBlockFork) {
#endif
// Regtest mode doesn't require peers
if (vNodes.empty() && chainparams.MiningRequiresPeers())
break;
if ((UintToArith256(pblock->nNonce) & 0xffff) == 0xffff)
break;
if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)
break;
#ifdef FORK_CB_INPUT
}
#endif
if (pindexPrev != chainActive.Tip())
break;
// Update nNonce and nTime
pblock->nNonce = ArithToUint256(UintToArith256(pblock->nNonce) + 1);
UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
if (chainparams.GetConsensus().fPowAllowMinDifficultyBlocks)
{
// Changing pblock->nTime can change work required on testnet:
hashTarget.SetCompact(pblock->nBits);
#ifdef FORK_CB_INPUT
if (!isNextBlockFork) {
#endif
if (chainparams.GetConsensus().fPowAllowMinDifficultyBlocks)
{
// Changing pblock->nTime can change work required on testnet:
hashTarget.SetCompact(pblock->nBits);
}
#ifdef FORK_CB_INPUT
}
#endif
}
}
}
@ -716,14 +940,14 @@ void static BitcoinMiner()
{
miningTimer.stop();
c.disconnect();
LogPrintf("ZcashMiner terminated\n");
LogPrintf("BTCPrivate Miner terminated\n");
throw;
}
catch (const std::runtime_error &e)
{
miningTimer.stop();
c.disconnect();
LogPrintf("ZcashMiner runtime error: %s\n", e.what());
LogPrintf("BTCPrivate Miner runtime error: %s\n", e.what());
return;
}
miningTimer.stop();

View File

@ -49,4 +49,8 @@ void GenerateBitcoins(bool fGenerate, int nThreads);
void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev);
#ifdef FORK_CB_INPUT
CBlockTemplate* CreateNewForkBlock();
#endif
#endif // BITCOIN_MINER_H

View File

@ -272,7 +272,11 @@ bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) {
return true;
}
#ifdef FORK_CB_INPUT
bool CBlockTreeDB::LoadBlockIndexGuts(int64_t forkStart, int64_t forkStop)
#else
bool CBlockTreeDB::LoadBlockIndexGuts()
#endif
{
boost::scoped_ptr<leveldb::Iterator> pcursor(NewIterator());
@ -311,8 +315,14 @@ bool CBlockTreeDB::LoadBlockIndexGuts()
pindexNew->nStatus = diskindex.nStatus;
pindexNew->nTx = diskindex.nTx;
#ifdef FORK_CB_INPUT
if (pindexNew->nHeight < forkStart || pindexNew->nHeight >= forkStop) {
#endif
if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, Params().GetConsensus()))
return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew->ToString());
#ifdef FORK_CB_INPUT
}
#endif
pcursor->Next();
} else {

View File

@ -67,7 +67,13 @@ public:
bool WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> > &list);
bool WriteFlag(const std::string &name, bool fValue);
bool ReadFlag(const std::string &name, bool &fValue);
#ifdef FORK_CB_INPUT
bool LoadBlockIndexGuts(int64_t forkStart, int64_t forkStop);
#else
bool LoadBlockIndexGuts();
#endif
};
#endif // BITCOIN_TXDB_H