mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-26 19:23:26 -03:00
New reject p2p message
This commit is contained in:
parent
17faf56262
commit
358ce2664d
2 changed files with 146 additions and 50 deletions
169
src/main.cpp
169
src/main.cpp
|
@ -18,6 +18,7 @@
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include <sstream>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string/replace.hpp>
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
|
@ -667,24 +668,30 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
|
||||||
{
|
{
|
||||||
// Basic checks that don't depend on any context
|
// Basic checks that don't depend on any context
|
||||||
if (tx.vin.empty())
|
if (tx.vin.empty())
|
||||||
return state.DoS(10, error("CheckTransaction() : vin empty"));
|
return state.DoS(10, error("CheckTransaction() : vin empty"),
|
||||||
|
REJECT_INVALID, "vin empty");
|
||||||
if (tx.vout.empty())
|
if (tx.vout.empty())
|
||||||
return state.DoS(10, error("CheckTransaction() : vout empty"));
|
return state.DoS(10, error("CheckTransaction() : vout empty"),
|
||||||
|
REJECT_INVALID, "vout empty");
|
||||||
// Size limits
|
// Size limits
|
||||||
if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
|
if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
|
||||||
return state.DoS(100, error("CTransaction::CheckTransaction() : size limits failed"));
|
return state.DoS(100, error("CTransaction::CheckTransaction() : size limits failed"),
|
||||||
|
REJECT_INVALID, "oversize");
|
||||||
|
|
||||||
// Check for negative or overflow output values
|
// Check for negative or overflow output values
|
||||||
int64_t nValueOut = 0;
|
int64_t nValueOut = 0;
|
||||||
BOOST_FOREACH(const CTxOut& txout, tx.vout)
|
BOOST_FOREACH(const CTxOut& txout, tx.vout)
|
||||||
{
|
{
|
||||||
if (txout.nValue < 0)
|
if (txout.nValue < 0)
|
||||||
return state.DoS(100, error("CheckTransaction() : txout.nValue negative"));
|
return state.DoS(100, error("CheckTransaction() : txout.nValue negative"),
|
||||||
|
REJECT_INVALID, "vout negative");
|
||||||
if (txout.nValue > MAX_MONEY)
|
if (txout.nValue > MAX_MONEY)
|
||||||
return state.DoS(100, error("CheckTransaction() : txout.nValue too high"));
|
return state.DoS(100, error("CheckTransaction() : txout.nValue too high"),
|
||||||
|
REJECT_INVALID, "vout too large");
|
||||||
nValueOut += txout.nValue;
|
nValueOut += txout.nValue;
|
||||||
if (!MoneyRange(nValueOut))
|
if (!MoneyRange(nValueOut))
|
||||||
return state.DoS(100, error("CTransaction::CheckTransaction() : txout total out of range"));
|
return state.DoS(100, error("CTransaction::CheckTransaction() : txout total out of range"),
|
||||||
|
REJECT_INVALID, "txout total too large");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for duplicate inputs
|
// Check for duplicate inputs
|
||||||
|
@ -692,20 +699,23 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
|
||||||
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
||||||
{
|
{
|
||||||
if (vInOutPoints.count(txin.prevout))
|
if (vInOutPoints.count(txin.prevout))
|
||||||
return state.DoS(100, error("CTransaction::CheckTransaction() : duplicate inputs"));
|
return state.DoS(100, error("CTransaction::CheckTransaction() : duplicate inputs"),
|
||||||
|
REJECT_INVALID, "duplicate inputs");
|
||||||
vInOutPoints.insert(txin.prevout);
|
vInOutPoints.insert(txin.prevout);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tx.IsCoinBase())
|
if (tx.IsCoinBase())
|
||||||
{
|
{
|
||||||
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
|
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
|
||||||
return state.DoS(100, error("CheckTransaction() : coinbase script size"));
|
return state.DoS(100, error("CheckTransaction() : coinbase script size"),
|
||||||
|
REJECT_INVALID, "coinbase script too large");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
||||||
if (txin.prevout.IsNull())
|
if (txin.prevout.IsNull())
|
||||||
return state.DoS(10, error("CheckTransaction() : prevout is null"));
|
return state.DoS(10, error("CheckTransaction() : prevout is null"),
|
||||||
|
REJECT_INVALID, "prevout null");
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -758,13 +768,15 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
||||||
|
|
||||||
// Coinbase is only valid in a block, not as a loose transaction
|
// Coinbase is only valid in a block, not as a loose transaction
|
||||||
if (tx.IsCoinBase())
|
if (tx.IsCoinBase())
|
||||||
return state.DoS(100, error("AcceptToMemoryPool: : coinbase as individual tx"));
|
return state.DoS(100, error("AcceptToMemoryPool: : coinbase as individual tx"),
|
||||||
|
REJECT_INVALID, "coinbase");
|
||||||
|
|
||||||
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
|
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
|
||||||
string reason;
|
string reason;
|
||||||
if (Params().NetworkID() == CChainParams::MAIN && !IsStandardTx(tx, reason))
|
if (Params().NetworkID() == CChainParams::MAIN && !IsStandardTx(tx, reason))
|
||||||
return error("AcceptToMemoryPool: : nonstandard transaction: %s",
|
return state.DoS(0,
|
||||||
reason.c_str());
|
error("AcceptToMemoryPool : nonstandard transaction: %s", reason.c_str()),
|
||||||
|
REJECT_NONSTANDARD, reason);
|
||||||
|
|
||||||
// is it already in the memory pool?
|
// is it already in the memory pool?
|
||||||
uint256 hash = tx.GetHash();
|
uint256 hash = tx.GetHash();
|
||||||
|
@ -828,7 +840,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
||||||
|
|
||||||
// are the actual inputs available?
|
// are the actual inputs available?
|
||||||
if (!view.HaveInputs(tx))
|
if (!view.HaveInputs(tx))
|
||||||
return state.Invalid(error("AcceptToMemoryPool: : inputs already spent"));
|
return state.Invalid(error("AcceptToMemoryPool : inputs already spent"),
|
||||||
|
REJECT_DUPLICATE, "inputs spent");
|
||||||
|
|
||||||
// Bring the best block into scope
|
// Bring the best block into scope
|
||||||
view.GetBestBlock();
|
view.GetBestBlock();
|
||||||
|
@ -851,9 +864,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
||||||
// Don't accept it if it can't get into a block
|
// Don't accept it if it can't get into a block
|
||||||
int64_t txMinFee = GetMinFee(tx, true, GMF_RELAY);
|
int64_t txMinFee = GetMinFee(tx, true, GMF_RELAY);
|
||||||
if (fLimitFree && nFees < txMinFee)
|
if (fLimitFree && nFees < txMinFee)
|
||||||
return error("AcceptToMemoryPool: : not enough fees %s, %"PRId64" < %"PRId64,
|
return state.DoS(0, error("AcceptToMemoryPool : not enough fees %s, %"PRId64" < %"PRId64,
|
||||||
hash.ToString().c_str(),
|
hash.ToString().c_str(), nFees, txMinFee),
|
||||||
nFees, txMinFee);
|
REJECT_INSUFFICIENTFEE, "insufficient fee");
|
||||||
|
|
||||||
// Continuously rate-limit free transactions
|
// Continuously rate-limit free transactions
|
||||||
// This mitigates 'penny-flooding' -- sending thousands of free transactions just to
|
// This mitigates 'penny-flooding' -- sending thousands of free transactions just to
|
||||||
|
@ -873,7 +886,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
||||||
// -limitfreerelay unit is thousand-bytes-per-minute
|
// -limitfreerelay unit is thousand-bytes-per-minute
|
||||||
// At default rate it would take over a month to fill 1GB
|
// At default rate it would take over a month to fill 1GB
|
||||||
if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000)
|
if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000)
|
||||||
return error("AcceptToMemoryPool: : free transaction rejected by rate limiter");
|
return state.DoS(0, error("AcceptToMemoryPool : free transaction rejected by rate limiter"),
|
||||||
|
REJECT_INSUFFICIENTFEE, "insufficient priority");
|
||||||
LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize);
|
LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize);
|
||||||
dFreeCount += nSize;
|
dFreeCount += nSize;
|
||||||
}
|
}
|
||||||
|
@ -1509,26 +1523,32 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach
|
||||||
// If prev is coinbase, check that it's matured
|
// If prev is coinbase, check that it's matured
|
||||||
if (coins.IsCoinBase()) {
|
if (coins.IsCoinBase()) {
|
||||||
if (nSpendHeight - coins.nHeight < COINBASE_MATURITY)
|
if (nSpendHeight - coins.nHeight < COINBASE_MATURITY)
|
||||||
return state.Invalid(error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight));
|
return state.Invalid(
|
||||||
|
error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight),
|
||||||
|
REJECT_INVALID, "premature spend of coinbase");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for negative or overflow input values
|
// Check for negative or overflow input values
|
||||||
nValueIn += coins.vout[prevout.n].nValue;
|
nValueIn += coins.vout[prevout.n].nValue;
|
||||||
if (!MoneyRange(coins.vout[prevout.n].nValue) || !MoneyRange(nValueIn))
|
if (!MoneyRange(coins.vout[prevout.n].nValue) || !MoneyRange(nValueIn))
|
||||||
return state.DoS(100, error("CheckInputs() : txin values out of range"));
|
return state.DoS(100, error("CheckInputs() : txin values out of range"),
|
||||||
|
REJECT_INVALID, "input values out of range");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nValueIn < GetValueOut(tx))
|
if (nValueIn < GetValueOut(tx))
|
||||||
return state.DoS(100, error("CheckInputs() : %s value in < value out", tx.GetHash().ToString().c_str()));
|
return state.DoS(100, error("CheckInputs() : %s value in < value out", tx.GetHash().ToString().c_str()),
|
||||||
|
REJECT_INVALID, "in < out");
|
||||||
|
|
||||||
// Tally transaction fees
|
// Tally transaction fees
|
||||||
int64_t nTxFee = nValueIn - GetValueOut(tx);
|
int64_t nTxFee = nValueIn - GetValueOut(tx);
|
||||||
if (nTxFee < 0)
|
if (nTxFee < 0)
|
||||||
return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", tx.GetHash().ToString().c_str()));
|
return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", tx.GetHash().ToString().c_str()),
|
||||||
|
REJECT_INVALID, "fee < 0");
|
||||||
nFees += nTxFee;
|
nFees += nTxFee;
|
||||||
if (!MoneyRange(nFees))
|
if (!MoneyRange(nFees))
|
||||||
return state.DoS(100, error("CheckInputs() : nFees out of range"));
|
return state.DoS(100, error("CheckInputs() : nFees out of range"),
|
||||||
|
REJECT_INVALID, "fee out of range");
|
||||||
|
|
||||||
// The first loop above does all the inexpensive checks.
|
// The first loop above does all the inexpensive checks.
|
||||||
// Only if ALL inputs pass do we perform expensive ECDSA signature checks.
|
// Only if ALL inputs pass do we perform expensive ECDSA signature checks.
|
||||||
|
@ -1553,9 +1573,9 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach
|
||||||
// encodings or not; if so, don't trigger DoS protection.
|
// encodings or not; if so, don't trigger DoS protection.
|
||||||
CScriptCheck check(coins, tx, i, flags & (~SCRIPT_VERIFY_STRICTENC), 0);
|
CScriptCheck check(coins, tx, i, flags & (~SCRIPT_VERIFY_STRICTENC), 0);
|
||||||
if (check())
|
if (check())
|
||||||
return state.Invalid();
|
return state.Invalid(false, REJECT_NONSTANDARD, "non-canonical");
|
||||||
}
|
}
|
||||||
return state.DoS(100,false);
|
return state.DoS(100,false, REJECT_NONSTANDARD, "non-canonical");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1723,7 +1743,8 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
|
||||||
for (unsigned int i = 0; i < block.vtx.size(); i++) {
|
for (unsigned int i = 0; i < block.vtx.size(); i++) {
|
||||||
uint256 hash = block.GetTxHash(i);
|
uint256 hash = block.GetTxHash(i);
|
||||||
if (view.HaveCoins(hash) && !view.GetCoins(hash).IsPruned())
|
if (view.HaveCoins(hash) && !view.GetCoins(hash).IsPruned())
|
||||||
return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction"));
|
return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction"),
|
||||||
|
REJECT_INVALID, "BIP30");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1752,12 +1773,14 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
|
||||||
nInputs += tx.vin.size();
|
nInputs += tx.vin.size();
|
||||||
nSigOps += GetLegacySigOpCount(tx);
|
nSigOps += GetLegacySigOpCount(tx);
|
||||||
if (nSigOps > MAX_BLOCK_SIGOPS)
|
if (nSigOps > MAX_BLOCK_SIGOPS)
|
||||||
return state.DoS(100, error("ConnectBlock() : too many sigops"));
|
return state.DoS(100, error("ConnectBlock() : too many sigops"),
|
||||||
|
REJECT_INVALID, "too many sigops");
|
||||||
|
|
||||||
if (!tx.IsCoinBase())
|
if (!tx.IsCoinBase())
|
||||||
{
|
{
|
||||||
if (!view.HaveInputs(tx))
|
if (!view.HaveInputs(tx))
|
||||||
return state.DoS(100, error("ConnectBlock() : inputs missing/spent"));
|
return state.DoS(100, error("ConnectBlock() : inputs missing/spent"),
|
||||||
|
REJECT_INVALID, "inputs missing/spent");
|
||||||
|
|
||||||
if (fStrictPayToScriptHash)
|
if (fStrictPayToScriptHash)
|
||||||
{
|
{
|
||||||
|
@ -1766,7 +1789,8 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
|
||||||
// an incredibly-expensive-to-validate block.
|
// an incredibly-expensive-to-validate block.
|
||||||
nSigOps += GetP2SHSigOpCount(tx, view);
|
nSigOps += GetP2SHSigOpCount(tx, view);
|
||||||
if (nSigOps > MAX_BLOCK_SIGOPS)
|
if (nSigOps > MAX_BLOCK_SIGOPS)
|
||||||
return state.DoS(100, error("ConnectBlock() : too many sigops"));
|
return state.DoS(100, error("ConnectBlock() : too many sigops"),
|
||||||
|
REJECT_INVALID, "too many sigops");
|
||||||
}
|
}
|
||||||
|
|
||||||
nFees += view.GetValueIn(tx)-GetValueOut(tx);
|
nFees += view.GetValueIn(tx)-GetValueOut(tx);
|
||||||
|
@ -1790,7 +1814,10 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
|
||||||
LogPrintf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)block.vtx.size(), 0.001 * nTime, 0.001 * nTime / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1));
|
LogPrintf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)block.vtx.size(), 0.001 * nTime, 0.001 * nTime / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1));
|
||||||
|
|
||||||
if (GetValueOut(block.vtx[0]) > GetBlockValue(pindex->nHeight, nFees))
|
if (GetValueOut(block.vtx[0]) > GetBlockValue(pindex->nHeight, nFees))
|
||||||
return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRId64" vs limit=%"PRId64")", GetValueOut(block.vtx[0]), GetBlockValue(pindex->nHeight, nFees)));
|
return state.DoS(100,
|
||||||
|
error("ConnectBlock() : coinbase pays too much (actual=%"PRId64" vs limit=%"PRId64")",
|
||||||
|
GetValueOut(block.vtx[0]), GetBlockValue(pindex->nHeight, nFees)),
|
||||||
|
REJECT_INVALID, "coinbase too large");
|
||||||
|
|
||||||
if (!control.Wait())
|
if (!control.Wait())
|
||||||
return state.DoS(100, false);
|
return state.DoS(100, false);
|
||||||
|
@ -2161,22 +2188,27 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo
|
||||||
|
|
||||||
// Size limits
|
// Size limits
|
||||||
if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
|
if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
|
||||||
return state.DoS(100, error("CheckBlock() : size limits failed"));
|
return state.DoS(100, error("CheckBlock() : size limits failed"),
|
||||||
|
REJECT_INVALID, "block size too large");
|
||||||
|
|
||||||
// Check proof of work matches claimed amount
|
// Check proof of work matches claimed amount
|
||||||
if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits))
|
if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits))
|
||||||
return state.DoS(50, error("CheckBlock() : proof of work failed"));
|
return state.DoS(50, error("CheckBlock() : proof of work failed"),
|
||||||
|
REJECT_INVALID, "invalid pow");
|
||||||
|
|
||||||
// Check timestamp
|
// Check timestamp
|
||||||
if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60)
|
if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60)
|
||||||
return state.Invalid(error("CheckBlock() : block timestamp too far in the future"));
|
return state.Invalid(error("CheckBlock() : block timestamp too far in the future"),
|
||||||
|
REJECT_INVALID, "time in future");
|
||||||
|
|
||||||
// First transaction must be coinbase, the rest must not be
|
// First transaction must be coinbase, the rest must not be
|
||||||
if (block.vtx.empty() || !block.vtx[0].IsCoinBase())
|
if (block.vtx.empty() || !block.vtx[0].IsCoinBase())
|
||||||
return state.DoS(100, error("CheckBlock() : first tx is not coinbase"));
|
return state.DoS(100, error("CheckBlock() : first tx is not coinbase"),
|
||||||
|
REJECT_INVALID, "no coinbase");
|
||||||
for (unsigned int i = 1; i < block.vtx.size(); i++)
|
for (unsigned int i = 1; i < block.vtx.size(); i++)
|
||||||
if (block.vtx[i].IsCoinBase())
|
if (block.vtx[i].IsCoinBase())
|
||||||
return state.DoS(100, error("CheckBlock() : more than one coinbase"));
|
return state.DoS(100, error("CheckBlock() : more than one coinbase"),
|
||||||
|
REJECT_INVALID, "duplicate coinbase");
|
||||||
|
|
||||||
// Check transactions
|
// Check transactions
|
||||||
BOOST_FOREACH(const CTransaction& tx, block.vtx)
|
BOOST_FOREACH(const CTransaction& tx, block.vtx)
|
||||||
|
@ -2195,7 +2227,8 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo
|
||||||
uniqueTx.insert(block.GetTxHash(i));
|
uniqueTx.insert(block.GetTxHash(i));
|
||||||
}
|
}
|
||||||
if (uniqueTx.size() != block.vtx.size())
|
if (uniqueTx.size() != block.vtx.size())
|
||||||
return state.DoS(100, error("CheckBlock() : duplicate transaction"), true);
|
return state.DoS(100, error("CheckBlock() : duplicate transaction"),
|
||||||
|
REJECT_INVALID, "duplicate transaction", true);
|
||||||
|
|
||||||
unsigned int nSigOps = 0;
|
unsigned int nSigOps = 0;
|
||||||
BOOST_FOREACH(const CTransaction& tx, block.vtx)
|
BOOST_FOREACH(const CTransaction& tx, block.vtx)
|
||||||
|
@ -2203,11 +2236,13 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo
|
||||||
nSigOps += GetLegacySigOpCount(tx);
|
nSigOps += GetLegacySigOpCount(tx);
|
||||||
}
|
}
|
||||||
if (nSigOps > MAX_BLOCK_SIGOPS)
|
if (nSigOps > MAX_BLOCK_SIGOPS)
|
||||||
return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"));
|
return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"),
|
||||||
|
REJECT_INVALID, "sig op count", true);
|
||||||
|
|
||||||
// Check merkle root
|
// Check merkle root
|
||||||
if (fCheckMerkleRoot && block.hashMerkleRoot != block.vMerkleTree.back())
|
if (fCheckMerkleRoot && block.hashMerkleRoot != block.vMerkleTree.back())
|
||||||
return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"));
|
return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"),
|
||||||
|
REJECT_INVALID, "bad merkle root", true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2231,20 +2266,24 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp)
|
||||||
|
|
||||||
// Check proof of work
|
// Check proof of work
|
||||||
if (block.nBits != GetNextWorkRequired(pindexPrev, &block))
|
if (block.nBits != GetNextWorkRequired(pindexPrev, &block))
|
||||||
return state.DoS(100, error("AcceptBlock() : incorrect proof of work"));
|
return state.DoS(100, error("AcceptBlock() : incorrect proof of work"),
|
||||||
|
REJECT_INVALID, "bad pow");
|
||||||
|
|
||||||
// Check timestamp against prev
|
// Check timestamp against prev
|
||||||
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast())
|
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast())
|
||||||
return state.Invalid(error("AcceptBlock() : block's timestamp is too early"));
|
return state.Invalid(error("AcceptBlock() : block's timestamp is too early"),
|
||||||
|
REJECT_INVALID, "timestamp too early");
|
||||||
|
|
||||||
// Check that all transactions are finalized
|
// Check that all transactions are finalized
|
||||||
BOOST_FOREACH(const CTransaction& tx, block.vtx)
|
BOOST_FOREACH(const CTransaction& tx, block.vtx)
|
||||||
if (!IsFinalTx(tx, nHeight, block.GetBlockTime()))
|
if (!IsFinalTx(tx, nHeight, block.GetBlockTime()))
|
||||||
return state.DoS(10, error("AcceptBlock() : contains a non-final transaction"));
|
return state.DoS(10, error("AcceptBlock() : contains a non-final transaction"),
|
||||||
|
REJECT_INVALID, "non-final tx");
|
||||||
|
|
||||||
// Check that the block chain matches the known block chain up to a checkpoint
|
// Check that the block chain matches the known block chain up to a checkpoint
|
||||||
if (!Checkpoints::CheckBlock(nHeight, hash))
|
if (!Checkpoints::CheckBlock(nHeight, hash))
|
||||||
return state.DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight));
|
return state.DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight),
|
||||||
|
REJECT_CHECKPOINT, "checkpoint mismatch");
|
||||||
|
|
||||||
// Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded:
|
// Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded:
|
||||||
if (block.nVersion < 2)
|
if (block.nVersion < 2)
|
||||||
|
@ -2252,7 +2291,8 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp)
|
||||||
if ((!TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 950, 1000)) ||
|
if ((!TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 950, 1000)) ||
|
||||||
(TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 75, 100)))
|
(TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 75, 100)))
|
||||||
{
|
{
|
||||||
return state.Invalid(error("AcceptBlock() : rejected nVersion=1 block"));
|
return state.Invalid(error("AcceptBlock() : rejected nVersion=1 block"),
|
||||||
|
REJECT_OBSOLETE, "version 1 blocks obsolete");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Enforce block.nVersion=2 rule that the coinbase starts with serialized block height
|
// Enforce block.nVersion=2 rule that the coinbase starts with serialized block height
|
||||||
|
@ -2265,7 +2305,8 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp)
|
||||||
CScript expect = CScript() << nHeight;
|
CScript expect = CScript() << nHeight;
|
||||||
if (block.vtx[0].vin[0].scriptSig.size() < expect.size() ||
|
if (block.vtx[0].vin[0].scriptSig.size() < expect.size() ||
|
||||||
!std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin()))
|
!std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin()))
|
||||||
return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase"));
|
return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase"),
|
||||||
|
REJECT_INVALID, "height incorrect in coinbase");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2355,7 +2396,8 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl
|
||||||
int64_t deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime;
|
int64_t deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime;
|
||||||
if (deltaTime < 0)
|
if (deltaTime < 0)
|
||||||
{
|
{
|
||||||
return state.DoS(100, error("ProcessBlock() : block with timestamp before last checkpoint"));
|
return state.DoS(100, error("ProcessBlock() : block with timestamp before last checkpoint"),
|
||||||
|
REJECT_CHECKPOINT, "timestamp before checkpoint");
|
||||||
}
|
}
|
||||||
CBigNum bnNewBlock;
|
CBigNum bnNewBlock;
|
||||||
bnNewBlock.SetCompact(pblock->nBits);
|
bnNewBlock.SetCompact(pblock->nBits);
|
||||||
|
@ -2363,7 +2405,8 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl
|
||||||
bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime));
|
bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime));
|
||||||
if (bnNewBlock > bnRequired)
|
if (bnNewBlock > bnRequired)
|
||||||
{
|
{
|
||||||
return state.DoS(100, error("ProcessBlock() : block with too little proof-of-work"));
|
return state.DoS(100, error("ProcessBlock() : block with too little proof-of-work"),
|
||||||
|
REJECT_INVALID, "invalid pow");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3061,7 +3104,6 @@ bool static AlreadyHave(const CInv& inv)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void static ProcessGetData(CNode* pfrom)
|
void static ProcessGetData(CNode* pfrom)
|
||||||
{
|
{
|
||||||
std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin();
|
std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin();
|
||||||
|
@ -3193,6 +3235,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||||
// Each connection can only send one version message
|
// Each connection can only send one version message
|
||||||
if (pfrom->nVersion != 0)
|
if (pfrom->nVersion != 0)
|
||||||
{
|
{
|
||||||
|
pfrom->PushMessage("reject", strCommand, REJECT_DUPLICATE, string("Duplicate version message"));
|
||||||
pfrom->Misbehaving(1);
|
pfrom->Misbehaving(1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -3206,6 +3249,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||||
{
|
{
|
||||||
// disconnect from peers older than this proto version
|
// disconnect from peers older than this proto version
|
||||||
LogPrintf("partner %s using obsolete version %i; disconnecting\n", pfrom->addr.ToString().c_str(), pfrom->nVersion);
|
LogPrintf("partner %s using obsolete version %i; disconnecting\n", pfrom->addr.ToString().c_str(), pfrom->nVersion);
|
||||||
|
pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE,
|
||||||
|
strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION));
|
||||||
pfrom->fDisconnect = true;
|
pfrom->fDisconnect = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -3589,8 +3634,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||||
}
|
}
|
||||||
int nDoS = 0;
|
int nDoS = 0;
|
||||||
if (state.IsInvalid(nDoS))
|
if (state.IsInvalid(nDoS))
|
||||||
|
{
|
||||||
|
pfrom->PushMessage("reject", strCommand, state.GetRejectCode(),
|
||||||
|
state.GetRejectReason(), inv.hash);
|
||||||
if (nDoS > 0)
|
if (nDoS > 0)
|
||||||
pfrom->Misbehaving(nDoS);
|
pfrom->Misbehaving(nDoS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -3612,8 +3661,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||||
mapAlreadyAskedFor.erase(inv);
|
mapAlreadyAskedFor.erase(inv);
|
||||||
int nDoS = 0;
|
int nDoS = 0;
|
||||||
if (state.IsInvalid(nDoS))
|
if (state.IsInvalid(nDoS))
|
||||||
|
{
|
||||||
|
pfrom->PushMessage("reject", strCommand, state.GetRejectCode(),
|
||||||
|
state.GetRejectReason(), inv.hash);
|
||||||
if (nDoS > 0)
|
if (nDoS > 0)
|
||||||
pfrom->Misbehaving(nDoS);
|
pfrom->Misbehaving(nDoS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -3809,6 +3862,29 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
else if (strCommand == "reject")
|
||||||
|
{
|
||||||
|
if (fDebug)
|
||||||
|
{
|
||||||
|
string strMsg; unsigned char ccode; string strReason;
|
||||||
|
vRecv >> strMsg >> ccode >> strReason;
|
||||||
|
|
||||||
|
ostringstream ss;
|
||||||
|
ss << strMsg << " code " << itostr(ccode) << ": " << strReason;
|
||||||
|
|
||||||
|
if (strMsg == "block" || strMsg == "tx")
|
||||||
|
{
|
||||||
|
uint256 hash;
|
||||||
|
vRecv >> hash;
|
||||||
|
ss << ": hash " << hash.ToString();
|
||||||
|
}
|
||||||
|
// Truncate to reasonable length and sanitize before printing:
|
||||||
|
string s = ss.str();
|
||||||
|
if (s.size() > 111) s.erase(111, string::npos);
|
||||||
|
LogPrint("net", "Reject %s\n", SanitizeString(s).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Ignore unknown commands for extensibility
|
// Ignore unknown commands for extensibility
|
||||||
|
@ -3907,6 +3983,7 @@ bool ProcessMessages(CNode* pfrom)
|
||||||
}
|
}
|
||||||
catch (std::ios_base::failure& e)
|
catch (std::ios_base::failure& e)
|
||||||
{
|
{
|
||||||
|
pfrom->PushMessage("reject", strCommand, REJECT_MALFORMED, string("error parsing message"));
|
||||||
if (strstr(e.what(), "end of data"))
|
if (strstr(e.what(), "end of data"))
|
||||||
{
|
{
|
||||||
// Allow exceptions from under-length message on vRecv
|
// Allow exceptions from under-length message on vRecv
|
||||||
|
|
27
src/main.h
27
src/main.h
|
@ -67,6 +67,16 @@ static const int fHaveUPnP = true;
|
||||||
static const int fHaveUPnP = false;
|
static const int fHaveUPnP = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/** "reject" message codes **/
|
||||||
|
static const unsigned char REJECT_MALFORMED = 0x01;
|
||||||
|
static const unsigned char REJECT_INVALID = 0x10;
|
||||||
|
static const unsigned char REJECT_OBSOLETE = 0x11;
|
||||||
|
static const unsigned char REJECT_DUPLICATE = 0x12;
|
||||||
|
static const unsigned char REJECT_NONSTANDARD = 0x40;
|
||||||
|
static const unsigned char REJECT_DUST = 0x41;
|
||||||
|
static const unsigned char REJECT_INSUFFICIENTFEE = 0x42;
|
||||||
|
static const unsigned char REJECT_CHECKPOINT = 0x43;
|
||||||
|
|
||||||
|
|
||||||
extern CScript COINBASE_FLAGS;
|
extern CScript COINBASE_FLAGS;
|
||||||
|
|
||||||
|
@ -926,19 +936,26 @@ private:
|
||||||
MODE_ERROR, // run-time error
|
MODE_ERROR, // run-time error
|
||||||
} mode;
|
} mode;
|
||||||
int nDoS;
|
int nDoS;
|
||||||
|
std::string strRejectReason;
|
||||||
|
unsigned char chRejectCode;
|
||||||
bool corruptionPossible;
|
bool corruptionPossible;
|
||||||
public:
|
public:
|
||||||
CValidationState() : mode(MODE_VALID), nDoS(0) {}
|
CValidationState() : mode(MODE_VALID), nDoS(0) {}
|
||||||
bool DoS(int level, bool ret = false, bool corruptionIn = false) {
|
bool DoS(int level, bool ret = false,
|
||||||
|
unsigned char chRejectCodeIn=0, std::string strRejectReasonIn="",
|
||||||
|
bool corruptionIn=false) {
|
||||||
|
chRejectCode = chRejectCodeIn;
|
||||||
|
strRejectReason = strRejectReasonIn;
|
||||||
|
corruptionPossible = corruptionIn;
|
||||||
if (mode == MODE_ERROR)
|
if (mode == MODE_ERROR)
|
||||||
return ret;
|
return ret;
|
||||||
nDoS += level;
|
nDoS += level;
|
||||||
mode = MODE_INVALID;
|
mode = MODE_INVALID;
|
||||||
corruptionPossible = corruptionIn;
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
bool Invalid(bool ret = false) {
|
bool Invalid(bool ret = false,
|
||||||
return DoS(0, ret);
|
unsigned char _chRejectCode=0, std::string _strRejectReason="") {
|
||||||
|
return DoS(0, ret, _chRejectCode, _strRejectReason);
|
||||||
}
|
}
|
||||||
bool Error() {
|
bool Error() {
|
||||||
mode = MODE_ERROR;
|
mode = MODE_ERROR;
|
||||||
|
@ -967,6 +984,8 @@ public:
|
||||||
bool CorruptionPossible() {
|
bool CorruptionPossible() {
|
||||||
return corruptionPossible;
|
return corruptionPossible;
|
||||||
}
|
}
|
||||||
|
unsigned char GetRejectCode() const { return chRejectCode; }
|
||||||
|
std::string GetRejectReason() const { return strRejectReason; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/** An in-memory indexed chain of blocks. */
|
/** An in-memory indexed chain of blocks. */
|
||||||
|
|
Loading…
Add table
Reference in a new issue