map<COutPoint, CInPoint> mapNextTx;
map<uint256, CBlockIndex*> mapBlockIndex;
-uint256 hashGenesisBlock("0x000000006d52486334316794cc38ffeb7ebf35a7ebd661fd39f5f46b0d001575");
+set<pair<COutPoint, unsigned int> > setStakeSeen;
+uint256 hashGenesisBlock("0x000000007c82d1f0aa2896b01bf533a8cc26a1f44790be4ceb4ecde7bee24add");
static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32);
const int nInitialBlockThreshold = 120; // Regard blocks up until N-threshold as "initial download"
CBlockIndex* pindexGenesisBlock = NULL;
map<uint256, CBlock*> mapOrphanBlocks;
multimap<uint256, CBlock*> mapOrphanBlocksByPrev;
+set<pair<COutPoint, unsigned int> > setStakeSeenOrphan;
map<uint256, CDataStream*> mapOrphanTransactions;
multimap<uint256, CDataStream*> mapOrphanTransactionsByPrev;
// Check for negative or overflow output values
int64 nValueOut = 0;
- for (int i = (IsCoinStake()? 1 : 0); i < vout.size(); i++)
+ for (int i = 0; i < vout.size(); i++)
{
const CTxOut& txout = vout[i];
+ if (txout.IsEmpty() && (!IsCoinBase()) && (!IsCoinStake()))
+ return DoS(100, error("CTransaction::CheckTransaction() : txout empty for user transaction"));
if (txout.nValue < 0)
return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative"));
if (txout.nValue > MAX_MONEY)
return pblock->GetHash();
}
+// ppcoin: find block wanted by given orphan block
+uint256 WantedByOrphan(const CBlock* pblockOrphan)
+{
+ // Work back to the first block in the orphan chain
+ while (mapOrphanBlocks.count(pblockOrphan->hashPrevBlock))
+ pblockOrphan = mapOrphanBlocks[pblockOrphan->hashPrevBlock];
+ return pblockOrphan->hashPrevBlock;
+}
+
int64 static GetProofOfWorkReward(unsigned int nBits)
{
CBigNum bnSubsidyLimit = 9999 * COIN; // subsidy amount for difficulty 1
return nSubsidy;
}
-static const int64 nTargetTimespan = 7 * 24 * 60 * 60; // one week
-static const int64 nTargetSpacing = 10 * 60;
-static const int64 nInterval = nTargetTimespan / nTargetSpacing;
-static const int64 nMaxClockDrift = 2 * 60 * 60; // 2 hours
+static const int64 nTargetTimespan = 7 * 24 * 60 * 60; // one week
+static const int64 nTargetSpacingStake = 10 * 60; // ten minutes
+static const int64 nTargetSpacingWorkMax = 2 * 60 * 60; // two hours
+static const int64 nMaxClockDrift = 2 * 60 * 60; // two hours
//
// minimum amount of work that could possibly be required nTime after
{
CBigNum bnResult;
bnResult.SetCompact(nBase);
+ bnResult *= 2;
while (nTime > 0 && bnResult < bnProofOfWorkLimit)
{
- // Maximum 400% adjustment...
- bnResult *= 4;
- // ... in best-case exactly 4-times-normal target time
- nTime -= nTargetTimespan*4;
+ // Maximum 200% adjustment per day...
+ bnResult *= 2;
+ nTime -= 24 * 60 * 60;
}
if (bnResult > bnProofOfWorkLimit)
bnResult = bnProofOfWorkLimit;
return bnResult.GetCompact();
}
-unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast)
+// ppcoin: find last block index up to pindex
+const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake)
+{
+ while (pindex && (pindex->IsProofOfStake() != fProofOfStake))
+ pindex = pindex->pprev;
+ return pindex;
+}
+
+unsigned int static GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake)
{
// Genesis block and first block
if (pindexLast == NULL || pindexLast->pprev == NULL)
return bnProofOfWorkLimit.GetCompact();
- int64 nActualSpacing = pindexLast->GetBlockTime() - pindexLast->pprev->GetBlockTime();
+ const CBlockIndex* pindexPrev = GetLastBlockIndex(pindexLast, fProofOfStake);
+ if (pindexPrev == NULL)
+ return bnProofOfWorkLimit.GetCompact();
+ const CBlockIndex* pindexPrevPrev = GetLastBlockIndex(pindexPrev->pprev, fProofOfStake);
+ if (pindexPrevPrev == NULL)
+ return bnProofOfWorkLimit.GetCompact();
+ int64 nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime();
// ppcoin: target change every block
// ppcoin: retarget with exponential moving toward target spacing
CBigNum bnNew;
- bnNew.SetCompact(pindexLast->nBits);
+ bnNew.SetCompact(pindexPrev->nBits);
+ int64 nTargetSpacing = fProofOfStake? nTargetSpacingStake : min(nTargetSpacingWorkMax, nTargetSpacingStake * (1 + pindexLast->nHeight - pindexPrev->nHeight));
+ int64 nInterval = nTargetTimespan / nTargetSpacing;
bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing);
bnNew /= ((nInterval + 1) * nTargetSpacing);
return false;
//// issue here: it doesn't know the version
- unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK) - 1 + GetSizeOfCompactSize(vtx.size());
+ unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK) - (2 * GetSizeOfCompactSize(0)) + GetSizeOfCompactSize(vtx.size());
map<uint256, CTxIndex> mapQueuedChanges;
int64 nFees = 0;
// ppcoin: fees are not collected by miners as in bitcoin
// ppcoin: fees are destroyed to compensate the entire network
- if (vtx[0].GetValueOut() > GetProofOfWorkReward(nBits))
+ if (IsProofOfWork() && vtx[0].GetValueOut() > GetProofOfWorkReward(nBits))
return false;
if (fDebug && GetBoolArg("-printcreation"))
printf("ConnectBlock() : destroy=%s nFees=%"PRI64d"\n", FormatMoney(nFees).c_str(), nFees);
return true;
}
-bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
+bool Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
{
printf("REORGANIZE\n");
}
if (!txdb.WriteHashBestChain(pindexNew->GetBlockHash()))
return error("Reorganize() : WriteHashBestChain failed");
- if (!txdb.WriteAutoCheckpoint(Checkpoints::GetNextAutoCheckpoint(pindexNew->nCheckpoint)))
- return error("Reorganize() : WriteAutoCheckpoint failed");
// Make sure it's successfully written to disk before changing memory structure
if (!txdb.TxnCommit())
if (pindexGenesisBlock == NULL && hash == hashGenesisBlock)
{
txdb.WriteHashBestChain(hash);
- txdb.WriteAutoCheckpoint(Checkpoints::GetNextAutoCheckpoint(pindexNew->nCheckpoint));
if (!txdb.TxnCommit())
return error("SetBestChain() : TxnCommit failed");
pindexGenesisBlock = pindexNew;
else if (hashPrevBlock == hashBestChain)
{
// Adding to current best branch
- if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash) || !txdb.WriteAutoCheckpoint(Checkpoints::GetNextAutoCheckpoint(pindexNew->nCheckpoint)))
+ if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash))
{
txdb.TxnAbort();
InvalidChainFound(pindexNew);
nBestChainTrust = pindexNew->nChainTrust;
nTimeBestReceived = GetTime();
nTransactionsUpdated++;
- Checkpoints::AdvanceAutoCheckpoint(pindexBest->nCheckpoint);
printf("SetBestChain: new best=%s height=%d trust=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, CBigNum(nBestChainTrust).ToString().c_str());
return true;
}
+// ppcoin: coinstake must meet hash target according to the protocol:
+// input 0 must meet the formula
+// hash(nBits + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime) < bnTarget * nCoinDay
+// this ensures that the chance of getting a coinstake is proportional to the
+// amount of coin age one owns.
+// The reason this hash is chosen is the following:
+// nBits: encodes all past block timestamps, making computing hash in advance
+// more difficult
+// txPrev.block.nTime: prevent nodes from guessing a good timestamp to
+// generate transaction for future advantage
+// txPrev.offset: offset of txPrev inside block, to reduce the chance of
+// nodes generating coinstake at the same time
+// txPrev.nTime: reduce the chance of nodes generating coinstake at the same
+// time
+// txPrev.vout.n: output number of txPrev, to reduce the chance of nodes
+// generating coinstake at the same time
+// block/tx hash should not be used here as they can be generated in vast
+// quantities so as to generate blocks faster, degrading the system back into
+// a proof-of-work situation.
+//
+bool CTransaction::CheckProofOfStake(unsigned int nBits) const
+{
+ CBigNum bnTargetPerCoinDay;
+ bnTargetPerCoinDay.SetCompact(nBits);
+
+ if (!IsCoinStake())
+ return true;
+
+ // Input 0 must match the stake hash target per coin age (nBits)
+ const CTxIn& txin = vin[0];
+
+ // First try finding the previous transaction in database
+ CTxDB txdb("r");
+ CTransaction txPrev;
+ CTxIndex txindex;
+ if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex))
+ return false; // previous transaction not in main chain
+ txdb.Close();
+ if (nTime < txPrev.nTime)
+ return false; // Transaction timestamp violation
+
+ // Verify signature
+ if (!VerifySignature(txPrev, *this, 0))
+ return DoS(100, error("CheckProofOfStake() : VerifySignature failed on coinstake %s", GetHash().ToString().c_str()));
+
+ // Read block header
+ CBlock block;
+ if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
+ return false; // unable to read block of previous transaction
+ if (block.GetBlockTime() + AUTO_CHECKPOINT_TRUST_SPAN > nTime)
+ return false; // only count coins from at least one week ago
+
+ int64 nValueIn = txPrev.vout[txin.prevout.n].nValue;
+ CBigNum bnCoinDay = CBigNum(nValueIn) * (nTime-txPrev.nTime) / COIN / (24 * 60 * 60);
+ // Calculate hash
+ CDataStream ss(SER_GETHASH, VERSION);
+ ss << nBits << block.nTime << (txindex.pos.nTxPos - txindex.pos.nBlockPos) << txPrev.nTime << txin.prevout.n << nTime;
+ if (CBigNum(Hash(ss.begin(), ss.end())) <= bnCoinDay * bnTargetPerCoinDay)
+ return true;
+ else
+ return DoS(100, error("CheckProofOfStake() : check target failed on coinstake %s", GetHash().ToString().c_str()));
+}
+
// ppcoin: total coin age spent in transaction, in the unit of coin-days.
// Only those coins last spent at least a week ago count. As those
// transactions not in main chain are not currently indexed so we
if (!pindexNew)
return error("AddToBlockIndex() : new CBlockIndex failed");
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
+ if (pindexNew->fProofOfStake)
+ setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime));
pindexNew->phashBlock = &((*mi).first);
map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(hashPrevBlock);
{
pindexNew->pprev = (*miPrev).second;
pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
-
- // ppcoin: compute chain checkpoint
- pindexNew->nCheckpoint = Checkpoints::GetNextChainCheckpoint(pindexNew->pprev);
- assert (pindexNew->nCheckpoint >= pindexNew->pprev->nCheckpoint);
}
// ppcoin: compute chain trust score
return DoS(100, error("CheckBlock() : size limits failed"));
// Check proof of work matches claimed amount
- if (!CheckProofOfWork(GetHash(), nBits))
+ if (IsProofOfWork() && !CheckProofOfWork(GetHash(), nBits))
return DoS(50, error("CheckBlock() : proof of work failed"));
// Check timestamp
if (vtx[i].IsCoinStake())
return DoS(100, error("CheckBlock() : coinstake in wrong position"));
+ // ppcoin: coinbase output should be empty if proof-of-stake block
+ if (IsProofOfStake() && !vtx[0].vout[0].IsEmpty())
+ return error("CheckBlock() : coinbase output not empty for proof-of-stake block");
+
// Check coinbase timestamp
if (GetBlockTime() > (int64)vtx[0].nTime + nMaxClockDrift)
return DoS(50, error("CheckBlock() : coinbase timestamp is too early"));
+ // Check coinstake timestamp
+ if (IsProofOfStake() && GetBlockTime() > (int64)vtx[1].nTime + nMaxClockDrift)
+ return DoS(50, error("CheckBlock() : coinstake timestamp is too early"));
+
// Check transactions
BOOST_FOREACH(const CTransaction& tx, vtx)
{
if (hashMerkleRoot != BuildMerkleTree())
return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"));
- // Check block signature
+ // ppcoin: check block signature
if (!CheckBlockSignature())
return DoS(100, error("CheckBlock() : bad block signature"));
CBlockIndex* pindexPrev = (*mi).second;
int nHeight = pindexPrev->nHeight+1;
- // Check proof of work
- if (nBits != GetNextWorkRequired(pindexPrev))
- return DoS(100, error("AcceptBlock() : incorrect proof of work"));
+ // Check proof-of-work or proof-of-stake
+ if (nBits != GetNextTargetRequired(pindexPrev, IsProofOfStake()))
+ return DoS(100, error("AcceptBlock() : incorrect proof-of-work/proof-of-stake"));
// Check timestamp against prev
if (GetBlockTime() <= pindexPrev->GetMedianTimePast() || GetBlockTime() + nMaxClockDrift < pindexPrev->GetBlockTime())
if (!Checkpoints::CheckHardened(nHeight, hash))
return DoS(100, error("AcceptBlock() : rejected by hardened checkpoint lockin at %d", nHeight));
- // ppcoin: check that the block satisfies automatic checkpoint
- if (!Checkpoints::CheckAuto(pindexPrev))
- return DoS(100, error("AcceptBlock() : rejected by automatic checkpoint at %d", Checkpoints::nAutoCheckpoint));
+ // ppcoin: check that the block satisfies synchronized checkpoint
+ if (!Checkpoints::CheckSync(hash, pindexPrev))
+ return error("AcceptBlock() : rejected by synchronized checkpoint");
// Write block to history file
if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK)))
if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 140700))
pnode->PushInventory(CInv(MSG_BLOCK, hash));
+ // ppcoin: check pending sync-checkpoint
+ Checkpoints::AcceptPendingSyncCheckpoint();
+
return true;
}
if (mapOrphanBlocks.count(hash))
return error("ProcessBlock() : already have block (orphan) %s", hash.ToString().substr(0,20).c_str());
+ // ppcoin: check proof-of-stake
+ // Limited duplicity on stake: prevents block flood attack
+ // Duplicate stake allowed only when there is orphan child block
+ if (pblock->IsProofOfStake() && setStakeSeen.count(pblock->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
+ return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for block %s", pblock->GetProofOfStake().first.ToString().c_str(), pblock->GetProofOfStake().second, hash.ToString().c_str());
+
// Preliminary checks
if (!pblock->CheckBlock())
return error("ProcessBlock() : CheckBlock FAILED");
- CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex);
- if (pcheckpoint && pblock->hashPrevBlock != hashBestChain)
+ // ppcoin: verify hash target and signature of coinstake tx
+ if (pblock->IsProofOfStake() && !pblock->vtx[1].CheckProofOfStake(pblock->nBits))
+ return error("ProcessBlock() : check proof-of-stake failed for block %s", hash.ToString().c_str());
+
+ CBlockIndex* pcheckpoint = Checkpoints::GetLastSyncCheckpoint();
+ if (pcheckpoint && pblock->hashPrevBlock != hashBestChain && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
{
// Extra checks to prevent "fill up memory by spamming with bogus blocks"
int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime;
- if (deltaTime < 0)
- {
- pfrom->Misbehaving(100);
- return error("ProcessBlock() : block with timestamp before last checkpoint");
- }
CBigNum bnNewBlock;
bnNewBlock.SetCompact(pblock->nBits);
CBigNum bnRequired;
- bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime));
+ bnRequired.SetCompact(ComputeMinWork(GetLastBlockIndex(pcheckpoint, pblock->IsProofOfStake())->nBits, deltaTime));
+
if (bnNewBlock > bnRequired)
{
pfrom->Misbehaving(100);
- return error("ProcessBlock() : block with too little proof-of-work");
+ return error("ProcessBlock() : block with too little %s", pblock->IsProofOfStake()? "proof-of-stake" : "proof-of-work");
}
}
{
printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,20).c_str());
CBlock* pblock2 = new CBlock(*pblock);
+ // ppcoin: check proof-of-stake
+ if (pblock2->IsProofOfStake())
+ {
+ // Limited duplicity on stake: prevents block flood attack
+ // Duplicate stake allowed only when there is orphan child block
+ if (setStakeSeenOrphan.count(pblock2->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
+ return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for orphan block %s", pblock2->GetProofOfStake().first.ToString().c_str(), pblock2->GetProofOfStake().second, hash.ToString().c_str());
+ else
+ setStakeSeenOrphan.insert(pblock2->GetProofOfStake());
+ }
mapOrphanBlocks.insert(make_pair(hash, pblock2));
mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2));
// Ask this guy to fill in what we're missing
if (pfrom)
+ {
pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(pblock2));
+ // ppcoin: getblocks may not obtain the ancestor block rejected
+ // earlier by duplicate-stake check so we ask for it again directly
+ pfrom->AskFor(CInv(MSG_BLOCK, WantedByOrphan(pblock2)));
+ }
return true;
}
if (pblockOrphan->AcceptBlock())
vWorkQueue.push_back(pblockOrphan->GetHash());
mapOrphanBlocks.erase(pblockOrphan->GetHash());
+ setStakeSeenOrphan.erase(pblockOrphan->GetProofOfStake());
delete pblockOrphan;
}
mapOrphanBlocksByPrev.erase(hashPrev);
// Genesis block
const char* pszTimestamp = "MarketWatch 07/Nov/2011 Gold tops $1,790 to end at over six-week high";
CTransaction txNew;
- txNew.nTime = 1325878371;
+ txNew.nTime = 1339538219;
txNew.vin.resize(1);
txNew.vout.resize(1);
txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(4) << vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
- txNew.vout[0].nValue = GetProofOfWorkReward(bnProofOfWorkLimit.GetCompact());
- txNew.vout[0].scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
+ txNew.vout[0].SetEmpty();
CBlock block;
block.vtx.push_back(txNew);
block.hashPrevBlock = 0;
block.hashMerkleRoot = block.BuildMerkleTree();
block.nVersion = 1;
- block.nTime = 1325882536;
+ block.nTime = 1339540307;
block.nBits = bnProofOfWorkLimit.GetCompact();
- block.nNonce = 2081920190;
+ block.nNonce = 1281822831;
if (fTestNet)
{
printf("%s\n", block.GetHash().ToString().c_str());
printf("%s\n", hashGenesisBlock.ToString().c_str());
printf("%s\n", block.hashMerkleRoot.ToString().c_str());
- assert(block.hashMerkleRoot == uint256("0xc7311b56de266580cca65be108ae53d7100b5c3b17da8b1106044103abd7a521"));
+ assert(block.hashMerkleRoot == uint256("0x1557f46a17fcf8843dbe4c0c0edfd1d17eeff2c3c48d73a59d11f5d176e4b54d"));
block.print();
assert(block.GetHash() == hashGenesisBlock);
assert(block.CheckBlock());
return error("LoadBlockIndex() : writing genesis block to disk failed");
if (!block.AddToBlockIndex(nFile, nBlockPos))
return error("LoadBlockIndex() : genesis block not accepted");
+
+ // ppcoin: initialize synchronized checkpoint
+ if (!Checkpoints::WriteSyncCheckpoint(hashGenesisBlock))
+ return error("LoadBlockIndex() : failed to init sync checkpoint");
+ }
+
+ // ppcoin: if checkpoint master key changed must reset sync-checkpoint
+ {
+ CTxDB txdb;
+ string strPubKey = "";
+ if (!txdb.ReadCheckpointPubKey(strPubKey) || strPubKey != CSyncCheckpoint::strMasterPubKey)
+ {
+ // write checkpoint master key to db
+ txdb.TxnBegin();
+ if (!txdb.WriteCheckpointPubKey(CSyncCheckpoint::strMasterPubKey))
+ return error("LoadBlockIndex() : failed to write new checkpoint master key to db");
+ if (!txdb.TxnCommit())
+ return error("LoadBlockIndex() : failed to commit new checkpoint master key to db");
+ if (!Checkpoints::ResetSyncCheckpoint())
+ return error("LoadBlockIndex() : failed to reset sync-checkpoint");
+ }
+ txdb.Close();
}
return true;
// print item
CBlock block;
block.ReadFromDisk(pindex);
- printf("%d (%u,%u) %s %s tx %d",
+ printf("%d (%u,%u) %s %08lx %s tx %d",
pindex->nHeight,
pindex->nFile,
pindex->nBlockPos,
block.GetHash().ToString().substr(0,20).c_str(),
+ block.nBits,
DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()).c_str(),
block.vtx.size());
strStatusBar = strRPC = "WARNING: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.";
}
+ if (Checkpoints::hashInvalidCheckpoint != 0)
+ {
+ nPriority = 3000;
+ strStatusBar = strRPC = "WARNING: Invalid checkpoint found! Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.";
+ }
+
// Alerts
CRITICAL_BLOCK(cs_mapAlerts)
{
BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts)
item.second.RelayTo(pfrom);
+ // ppcoin: relay sync-checkpoint
+ CRITICAL_BLOCK(Checkpoints::cs_hashSyncCheckpoint)
+ if (!Checkpoints::checkpointMessage.IsNull())
+ Checkpoints::checkpointMessage.RelayTo(pfrom);
+
pfrom->fSuccessfullyConnected = true;
printf("version message: version %d, blocks=%d\n", pfrom->nVersion, pfrom->nStartingHeight);
}
}
+ else if (strCommand == "checkpoint")
+ {
+ CSyncCheckpoint checkpoint;
+ vRecv >> checkpoint;
+
+ if (checkpoint.ProcessSyncCheckpoint(pfrom))
+ {
+ // Relay
+ pfrom->hashCheckpointKnown = checkpoint.hashCheckpoint;
+ CRITICAL_BLOCK(cs_vNodes)
+ BOOST_FOREACH(CNode* pnode, vNodes)
+ checkpoint.RelayTo(pnode);
+ }
+ }
else
{
};
-CBlock* CreateNewBlock(CWallet* pwallet)
+CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfWorkOnly)
{
- CBlockIndex* pindexPrev = pindexBest;
CReserveKey reservekey(pwallet);
// Create new block
pblock->vtx.push_back(txNew);
// ppcoin: if coinstake available add coinstake tx
- CTransaction txCoinStake;
- if (pwallet->CreateCoinStake(txNew.vout[0].scriptPubKey, txCoinStake))
- pblock->vtx.push_back(txCoinStake);
+ static unsigned int nLastCoinStakeCheckTime = GetAdjustedTime() - nMaxClockDrift + 60; // only initialized at startup
+ CBlockIndex* pindexPrev = pindexBest;
+
+ if (!fProofOfWorkOnly)
+ {
+ while (nLastCoinStakeCheckTime < GetAdjustedTime())
+ {
+ pindexPrev = pindexBest; // get best block again to avoid getting stale
+ pblock->nBits = GetNextTargetRequired(pindexPrev, true);
+ static CCriticalSection cs;
+ CTransaction txCoinStake;
+ CRITICAL_BLOCK(cs)
+ {
+ // mining may have been suspended for a while so
+ // need to take max to satisfy the timestamp protocol
+ nLastCoinStakeCheckTime = max(++nLastCoinStakeCheckTime, (unsigned int) (GetAdjustedTime() - nMaxClockDrift + 60));
+ txCoinStake.nTime = nLastCoinStakeCheckTime;
+ }
+ if (pwallet->CreateCoinStake(pblock->nBits, txCoinStake))
+ {
+ pblock->vtx.push_back(txCoinStake);
+ pblock->vtx[0].vout[0].SetEmpty();
+ break;
+ }
+ }
+ }
+
+ pblock->nBits = GetNextTargetRequired(pindexPrev, pblock->IsProofOfStake());
// Collect memory pool transactions into the block
int64 nFees = 0;
}
}
}
- pblock->nBits = GetNextWorkRequired(pindexPrev);
- pblock->vtx[0].vout[0].nValue = GetProofOfWorkReward(pblock->nBits);
+ if (pblock->IsProofOfWork())
+ pblock->vtx[0].vout[0].nValue = GetProofOfWorkReward(pblock->nBits);
// Fill in header
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
uint256 hash = pblock->GetHash();
uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
- if (hash > hashTarget)
- return false;
+ if (hash > hashTarget && pblock->IsProofOfWork())
+ return error("BitcoinMiner : proof-of-work not meeting target");
//// debug print
printf("BitcoinMiner:\n");
- printf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str());
+ printf("new block found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str());
pblock->print();
printf("%s ", DateTimeStrFormat("%x %H:%M", GetTime()).c_str());
printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str());
auto_ptr<CBlock> pblock(CreateNewBlock(pwallet));
if (!pblock.get())
return;
+
IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce);
+ // ppcoin: if proof-of-stake block found then process block
+ if (pblock->IsProofOfStake())
+ {
+ if (!pblock->SignBlock(*pwalletMain))
+ {
+ error("BitcoinMiner: Unable to sign new proof-of-stake block");
+ return;
+ }
+ printf("BitcoinMiner : proof-of-stake block found %s\n", pblock->GetHash().ToString().c_str());
+ SetThreadPriority(THREAD_PRIORITY_NORMAL);
+ CheckWork(pblock.get(), *pwalletMain, reservekey);
+ SetThreadPriority(THREAD_PRIORITY_LOWEST);
+ continue;
+ }
+
printf("Running BitcoinMiner with %d transactions in block\n", pblock->vtx.size());
// Found a solution
pblock->nNonce = ByteReverse(nNonceFound);
assert(hash == pblock->GetHash());
- // should be able to sign block - assert here for now
- assert(pblock->SignBlock(*pwalletMain));
+ if (!pblock->SignBlock(*pwalletMain))
+ {
+ error("BitcoinMiner: Unable to sign new proof-of-work block");
+ return;
+ }
SetThreadPriority(THREAD_PRIORITY_NORMAL);
CheckWork(pblock.get(), *pwalletMain, reservekey);