if (nVersion > CTransaction::CURRENT_VERSION)
return false;
+ unsigned int nDataOut = 0;
+ txnouttype whichType;
BOOST_FOREACH(const CTxIn& txin, vin)
{
// Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG
return false;
if (!txin.scriptSig.IsPushOnly())
return false;
- if (fEnforceCanonical && !txin.scriptSig.HasCanonicalPushes()) {
+ if (!txin.scriptSig.HasCanonicalPushes()) {
return false;
}
}
BOOST_FOREACH(const CTxOut& txout, vout) {
- if (!::IsStandard(txout.scriptPubKey))
- return false;
- if (txout.nValue == 0)
- return false;
- if (fEnforceCanonical && !txout.scriptPubKey.HasCanonicalPushes()) {
+ if (!::IsStandard(txout.scriptPubKey, whichType)) {
return false;
}
+ if (whichType == TX_NULL_DATA)
+ nDataOut++;
+ else {
+ if (txout.nValue == 0) {
+ return false;
+ }
+ if (!txout.scriptPubKey.HasCanonicalPushes()) {
+ return false;
+ }
+ }
}
+
+ // only one OP_RETURN txout is permitted
+ if (nDataOut > 1) {
+ return false;
+ }
+
return true;
}
// beside "push data" in the scriptSig the
// IsStandard() call returns false
vector<vector<unsigned char> > stack;
- if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0))
+ if (!EvalScript(stack, vin[i].scriptSig, *this, i, false, 0))
return false;
if (whichType == TX_SCRIPTHASH)
// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
- if (!tx.ConnectInputs(txdb, mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false))
+ if (!tx.ConnectInputs(txdb, mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, STANDARD_SCRIPT_VERIFY_FLAGS))
{
return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
}
}
// miner's coin base reward based on nBits
-int64 GetProofOfWorkReward(unsigned int nBits)
+int64 GetProofOfWorkReward(unsigned int nBits, int64 nFees)
{
CBigNum bnSubsidyLimit = MAX_MINT_PROOF_OF_WORK;
if (fDebug && GetBoolArg("-printcreation"))
printf("GetProofOfWorkReward() : create=%s nBits=0x%08x nSubsidy=%"PRI64d"\n", FormatMoney(nSubsidy).c_str(), nBits, nSubsidy);
- return min(nSubsidy, MAX_MINT_PROOF_OF_WORK);
+ return min(nSubsidy, MAX_MINT_PROOF_OF_WORK) + nFees;
}
// miner's coin stake reward based on nBits and coin age spent (coin-days)
}
bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs, map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx,
- const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash)
+ const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, unsigned int flags)
{
// Take over previous transactions' spent pointers
// fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain
if (!(fBlock && (nBestHeight < Checkpoints::GetTotalBlocksEstimate())))
{
// Verify signature
- if (!VerifySignature(txPrev, *this, i, fStrictPayToScriptHash, 0))
+ if (!VerifySignature(txPrev, *this, i, flags, 0))
{
- // only during transition phase for P2SH: do not invoke anti-DoS code for
- // potentially old clients relaying bad P2SH transactions
- if (fStrictPayToScriptHash && VerifySignature(txPrev, *this, i, false, 0))
- return error("ConnectInputs() : %s P2SH VerifySignature failed", GetHash().ToString().substr(0,10).c_str());
-
- return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));
+ if (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS)
+ {
+ if (VerifySignature(txPrev, *this, i, flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, 0))
+ return error("ConnectInputs() : %s STANDARD_NOT_MANDATORY_VERIFY_FLAGS VerifySignature failed", GetHash().ToString().substr(0,10).c_str());
+ }
+ return DoS(100,error("ConnectInputs() : %s STANDARD_MANDATORY_VERIFY_FLAGS VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));
}
}
int64 nCalculatedReward = GetProofOfStakeReward(nCoinAge, pindexBlock->nBits, nTime) - GetMinFee(1, false, GMF_BLOCK, nTxSize) + CENT;
if (nReward > nCalculatedReward)
- return DoS(100, error("CheckInputs() : coinstake pays too much(actual=%"PRI64d" vs calculated=%"PRI64d")", nReward, nCalculatedReward));
+ return DoS(100, error("ConnectInputs() : coinstake pays too much(actual=%"PRI64d" vs calculated=%"PRI64d")", nReward, nCalculatedReward));
}
else
{
return false;
// Verify signature
- if (!VerifySignature(txPrev, *this, i, true, 0))
+ if (!VerifySignature(txPrev, *this, i, SCRIPT_VERIFY_NOCACHE | SCRIPT_VERIFY_P2SH, 0))
return error("ConnectInputs() : VerifySignature failed");
///// this is redundant with the mempool.mapNextTx stuff,
if (!CheckBlock(!fJustCheck, !fJustCheck, false))
return false;
+ bool fProtocol048 = fTestNet || VALIDATION_SWITCH_TIME < nTime;
+
// Do not allow blocks that contain transactions which 'overwrite' older transactions,
// unless those are already completely spent.
// If such overwrites are allowed, coinbases and transactions depending upon those
// two in the chain that violate it. This prevents exploiting the issue against nodes in their
// initial block download.
bool fEnforceBIP30 = true; // Always active in NovaCoin
- bool fStrictPayToScriptHash = true; // Always active in NovaCoin
//// issue here: it doesn't know the version
unsigned int nTxPos;
if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs, fInvalid))
return false;
- if (fStrictPayToScriptHash)
- {
- // Add in sigops done by pay-to-script-hash inputs;
- // this is to prevent a "rogue miner" from creating
- // an incredibly-expensive-to-validate block.
- nSigOps += tx.GetP2SHSigOpCount(mapInputs);
- if (nSigOps > MAX_BLOCK_SIGOPS)
- return DoS(100, error("ConnectBlock() : too many sigops"));
- }
+ // Add in sigops done by pay-to-script-hash inputs;
+ // this is to prevent a "rogue miner" from creating
+ // an incredibly-expensive-to-validate block.
+ nSigOps += tx.GetP2SHSigOpCount(mapInputs);
+ if (nSigOps > MAX_BLOCK_SIGOPS)
+ return DoS(100, error("ConnectBlock() : too many sigops"));
int64 nTxValueIn = tx.GetValueIn(mapInputs);
int64 nTxValueOut = tx.GetValueOut();
if (!tx.IsCoinStake())
nFees += nTxValueIn - nTxValueOut;
- if (!tx.ConnectInputs(txdb, mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fStrictPayToScriptHash))
+ if (!tx.ConnectInputs(txdb, mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, SCRIPT_VERIFY_NOCACHE | SCRIPT_VERIFY_P2SH))
return false;
}
mapQueuedChanges[hashTx] = CTxIndex(posThisTx, tx.vout.size());
}
- // ppcoin: track money supply and mint amount info
+ if (IsProofOfWork())
+ {
+ int64 nBlockReward = GetProofOfWorkReward(nBits, fProtocol048 ? nFees : 0);
+
+ // Check coinbase reward
+ if (vtx[0].GetValueOut() > nBlockReward)
+ return error("CheckBlock() : coinbase reward exceeded (actual=%"PRI64d" vs calculated=%"PRI64d")",
+ vtx[0].GetValueOut(),
+ nBlockReward);
+ }
+
+ // track money supply and mint amount info
pindex->nMint = nValueOut - nValueIn + nFees;
pindex->nMoneySupply = (pindex->pprev? pindex->pprev->nMoneySupply : 0) + nValueOut - nValueIn;
if (!txdb.WriteBlockIndex(CDiskBlockIndex(pindex)))
return error("Connect() : WriteBlockIndex for pindex failed");
- // ppcoin: fees are not collected by miners as in bitcoin
- // ppcoin: fees are destroyed to compensate the entire network
- if (fDebug && GetBoolArg("-printcreation"))
+ // fees are not collected by proof-of-stake miners
+ // fees are destroyed to compensate the entire network
+ if (fProtocol048 && fDebug && IsProofOfStake() && GetBoolArg("-printcreation"))
printf("ConnectBlock() : destroy=%s nFees=%"PRI64d"\n", FormatMoney(nFees).c_str(), nFees);
if (fJustCheck)
// ppcoin: compute stake modifier
uint64 nStakeModifier = 0;
bool fGeneratedStakeModifier = false;
- if (!ComputeNextStakeModifier(pindexNew->pprev, nStakeModifier, fGeneratedStakeModifier))
+ if (!ComputeNextStakeModifier(pindexNew, nStakeModifier, fGeneratedStakeModifier))
return error("AddToBlockIndex() : ComputeNextStakeModifier() failed");
pindexNew->SetStakeModifier(nStakeModifier, fGeneratedStakeModifier);
pindexNew->nStakeModifierChecksum = GetStakeModifierChecksum(pindexNew);
if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
return DoS(100, error("CheckBlock() : size limits failed"));
+ bool fProtocol048 = fTestNet || VALIDATION_SWITCH_TIME < nTime;
+
// Check proof of work matches claimed amount
if (fCheckPOW && IsProofOfWork() && !CheckProofOfWork(GetHash(), nBits))
return DoS(50, error("CheckBlock() : proof of work failed"));
// First transaction must be coinbase, the rest must not be
if (vtx.empty() || !vtx[0].IsCoinBase())
return DoS(100, error("CheckBlock() : first tx is not coinbase"));
+
+ if (!fProtocol048)
+ {
+ // Check coinbase timestamp
+ if (GetBlockTime() < (int64)vtx[0].nTime)
+ return DoS(100, error("CheckBlock() : coinbase timestamp violation"));
+ }
+ else
+ {
+ // Check coinbase timestamp
+ if (GetBlockTime() < PastDrift((int64)vtx[0].nTime))
+ return DoS(50, error("CheckBlock() : coinbase timestamp is too late"));
+ }
+
for (unsigned int i = 1; i < vtx.size(); i++)
+ {
if (vtx[i].IsCoinBase())
return DoS(100, error("CheckBlock() : more than one coinbase"));
- // Check coinbase timestamp
- if (GetBlockTime() > FutureDrift((int64)vtx[0].nTime))
- return DoS(50, error("CheckBlock() : coinbase timestamp is too early"));
+ // Check transaction timestamp
+ if (GetBlockTime() < (int64)vtx[i].nTime)
+ return DoS(50, error("CheckBlock() : block timestamp earlier than transaction timestamp"));
+ }
if (IsProofOfStake())
{
+ if (fProtocol048)
+ {
+ if (nNonce != 0)
+ return DoS(100, error("CheckBlock() : non-zero nonce in proof-of-stake block"));
+ }
+
// Coinbase output should be empty if proof-of-stake block
if (vtx[0].vout.size() != 1 || !vtx[0].vout[0].IsEmpty())
return DoS(100, error("CheckBlock() : coinbase output not empty for proof-of-stake block"));
return DoS(100, error("CheckBlock() : more than one coinstake"));
// Check coinstake timestamp
- if (!CheckCoinStakeTimestamp(GetBlockTime(), (int64)vtx[1].nTime))
+ if (GetBlockTime() != (int64)vtx[1].nTime)
return DoS(50, error("CheckBlock() : coinstake timestamp violation nTimeBlock=%"PRI64d" nTimeTx=%u", GetBlockTime(), vtx[1].nTime));
// NovaCoin: check proof-of-stake block signature
}
else
{
- int64 nReward = GetProofOfWorkReward(nBits);
- // Check coinbase reward
- if (vtx[0].GetValueOut() > nReward)
- return DoS(50, error("CheckBlock() : coinbase reward exceeded (actual=%"PRI64d" vs calculated=%"PRI64d")",
- vtx[0].GetValueOut(),
- nReward));
-
// Should we check proof-of-work block signature or not?
//
// * Always skip on TestNet
{
if (!tx.CheckTransaction())
return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed"));
-
- // ppcoin: check transaction timestamp
- if (GetBlockTime() < (int64)tx.nTime)
- return DoS(50, error("CheckBlock() : block timestamp earlier than transaction timestamp"));
}
// Check for duplicate txids. This is caught by ConnectInputs(),
if (!block.AddToBlockIndex(nFile, nBlockPos))
return error("LoadBlockIndex() : genesis block not accepted");
- // ppcoin: initialize synchronized checkpoint
+ // initialize synchronized checkpoint
if (!Checkpoints::WriteSyncCheckpoint((!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)))
return error("LoadBlockIndex() : failed to init sync checkpoint");
+
+ // upgrade time set to zero if txdb initialized
+ {
+ if (!txdb.WriteModifierUpgradeTime(0))
+ return error("LoadBlockIndex() : failed to init upgrade info");
+ printf(" Upgrade Info: ModifierUpgradeTime txdb initialization\n");
+ }
}
string strPubKey = "";
return error("LoadBlockIndex() : failed to reset sync-checkpoint");
}
+ // upgrade time set to zero if blocktreedb initialized
+ if (txdb.ReadModifierUpgradeTime(nModifierUpgradeTime))
+ {
+ if (nModifierUpgradeTime)
+ printf(" Upgrade Info: blocktreedb upgrade detected at timestamp %d\n", nModifierUpgradeTime);
+ else
+ printf(" Upgrade Info: no blocktreedb upgrade detected.\n");
+ }
+ else
+ {
+ nModifierUpgradeTime = GetTime();
+ printf(" Upgrade Info: upgrading blocktreedb at timestamp %u\n", nModifierUpgradeTime);
+ if (!txdb.WriteModifierUpgradeTime(nModifierUpgradeTime))
+ return error("LoadBlockIndex() : failed to write upgrade info");
+ }
+
+
return true;
}
extern map<uint256, CAlert> mapAlerts;
extern CCriticalSection cs_mapAlerts;
-extern string strMintMessage;
-extern string strMintWarning;
-
string GetWarnings(string strFor)
{
int nPriority = 0;
if (GetBoolArg("-testsafemode"))
strRPC = "test";
- // wallet lock warning for minting
- if (strMintWarning != "")
- {
- nPriority = 0;
- strStatusBar = strMintWarning;
- }
-
// Misc warnings like out of disk space and clock is wrong
if (strMiscWarning != "")
{
strStatusBar = strMiscWarning;
}
+ // if detected unmet upgrade requirement enter safe mode
+ // Note: Modifier upgrade requires blockchain redownload if past protocol switch
+ if (IsFixedModifierInterval(nModifierUpgradeTime + 60*60*24)) // 1 day margin
+ {
+ nPriority = 5000;
+ strStatusBar = strRPC = "WARNING: Blockchain redownload required approaching or past v.0.4.4.6u4 upgrade deadline.";
+ }
+
// if detected invalid checkpoint enter safe mode
if (Checkpoints::hashInvalidCheckpoint != 0)
{