Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve logging of failures during block signature validation #889

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 25 additions & 9 deletions src/block_proof.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
#include <pow.h>

#include <chain.h>
#include <consensus/validation.h>
#include <primitives/block.h>
#include <script/interpreter.h>
#include <script/generic.hpp>
#include <util/system.h>
#include <validation.h>

bool CheckChallenge(const CBlockHeader& block, const CBlockIndex& indexLast, const Consensus::Params& params)
{
Expand All @@ -19,18 +22,18 @@ bool CheckChallenge(const CBlockHeader& block, const CBlockIndex& indexLast, con
}
}

static bool CheckProofGeneric(const CBlockHeader& block, const uint32_t max_block_signature_size, const CScript& challenge, const CScript& scriptSig, const CScriptWitness& witness)
static bool CheckProofGeneric(const CBlockHeader& block, CValidationState& state, const uint32_t max_block_signature_size, const CScript& challenge, const CScript& scriptSig, const CScriptWitness& witness)
{
// Legacy blocks have empty witness, dynafed blocks have empty scriptSig
bool is_dyna = !witness.stack.empty();

// Check signature limits for blocks
if (scriptSig.size() > max_block_signature_size) {
assert(!is_dyna);
return false;
return state.DoS(50, false, REJECT_INVALID, "block-signature-too-large");
} else if (witness.GetSerializedSize() > max_block_signature_size) {
assert(is_dyna);
return false;
return state.DoS(50, false, REJECT_INVALID, "block-signature-too-large");
}

// Some anti-DoS flags, though max_block_signature_size caps the possible
Expand All @@ -44,28 +47,41 @@ static bool CheckProofGeneric(const CBlockHeader& block, const uint32_t max_bloc
| SCRIPT_VERIFY_LOW_S // Stop easiest signature fiddling
| SCRIPT_VERIFY_WITNESS // Witness and to enforce cleanstack
| (is_dyna ? 0 : SCRIPT_NO_SIGHASH_BYTE); // Non-dynafed blocks do not have sighash byte
return GenericVerifyScript(scriptSig, witness, challenge, proof_flags, block);
if(!GenericVerifyScript(scriptSig, witness, challenge, proof_flags, block)) {
return state.DoS(50, false, REJECT_INVALID, "block-signature-verify-failed");
} else {
return true;
}
}

bool CheckProof(const CBlockHeader& block, const Consensus::Params& params)
bool CheckProof(const CBlockHeader& block, CValidationState& state, const Consensus::Params& params)
{
if (g_signed_blocks) {
const DynaFedParams& dynafed_params = block.m_dynafed_params;
if (dynafed_params.IsNull()) {
return CheckProofGeneric(block, params.max_block_signature_size, params.signblockscript, block.proof.solution, CScriptWitness());
return CheckProofGeneric(block, state, params.max_block_signature_size, params.signblockscript, block.proof.solution, CScriptWitness());
} else {
return CheckProofGeneric(block, dynafed_params.m_current.m_signblock_witness_limit, dynafed_params.m_current.m_signblockscript, CScript(), block.m_signblock_witness);
return CheckProofGeneric(block, state, dynafed_params.m_current.m_signblock_witness_limit, dynafed_params.m_current.m_signblockscript, CScript(), block.m_signblock_witness);
}
} else {
return CheckProofOfWork(block.GetHash(), block.nBits, params);
if(!CheckProofOfWork(block.GetHash(), block.nBits, params)) {
return state.DoS(50, false, REJECT_INVALID, "high-hash", false, "proof of work failed");
} else {
return true;
}
}
}

bool CheckProofSignedParent(const CBlockHeader& block, const Consensus::Params& params)
{
CValidationState state;
const DynaFedParams& dynafed_params = block.m_dynafed_params;
if (dynafed_params.IsNull()) {
return CheckProofGeneric(block, params.max_block_signature_size, params.parent_chain_signblockscript, block.proof.solution, CScriptWitness());
if(!CheckProofGeneric(block, state, params.max_block_signature_size, params.parent_chain_signblockscript, block.proof.solution, CScriptWitness())) {
return error("%s: CheckProofGeneric: %s", __func__, FormatStateMessage(state));
} else {
return true;
}
} else {
// Dynamic federations means we cannot validate the signer set
// at least without tracking the parent chain more directly.
Expand Down
3 changes: 2 additions & 1 deletion src/block_proof.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ class CBlockHeader;
class CBlockIndex;
class CProof;
class CScript;
class CValidationState;

// Elements signed chain functionality

/** Check on header proof, depending on chain type, PoW or signed **/
bool CheckProof(const CBlockHeader& block, const Consensus::Params&);
bool CheckProof(const CBlockHeader& block, CValidationState& state, const Consensus::Params&);
bool CheckProofSignedParent(const CBlockHeader& block, const Consensus::Params&);
bool CheckChallenge(const CBlockHeader& block, const CBlockIndex& indexLast, const Consensus::Params&);

Expand Down
3 changes: 2 additions & 1 deletion src/rpc/mining.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1189,7 +1189,8 @@ UniValue combineblocksigs(const JSONRPCRequest& request)
ssBlock << block;
UniValue result(UniValue::VOBJ);
result.pushKV("hex", HexStr(ssBlock.begin(), ssBlock.end()));
result.pushKV("complete", CheckProof(block, params));
CValidationState state; // ignored
result.pushKV("complete", CheckProof(block, state, params));
return result;
}

Expand Down
7 changes: 5 additions & 2 deletions src/txdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
#include <txdb.h>

#include <chainparams.h>
#include <consensus/validation.h>
#include <hash.h>
#include <random.h>
#include <pow.h>
#include <shutdown.h>
#include <uint256.h>
#include <util/system.h>
#include <ui_interface.h>
#include <validation.h>

#include <stdint.h>

Expand Down Expand Up @@ -323,10 +325,11 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams,

const uint256 block_hash = pindexNew->GetBlockHash();
// Only validate one of every 1000 block header for sanity check
CValidationState state;
if (pindexNew->nHeight % 1000 == 0 &&
block_hash != consensusParams.hashGenesisBlock &&
!CheckProof(pindexNew->GetBlockHeader(), consensusParams)) {
return error("%s: CheckProof: %s, %s", __func__, block_hash.ToString(), pindexNew->ToString());
!CheckProof(pindexNew->GetBlockHeader(), state, consensusParams)) {
return error("%s: CheckProof: (%s, %s): %s", __func__, block_hash.ToString(), pindexNew->ToString(), FormatStateMessage(state));
}
pcursor->Next();
} else {
Expand Down
12 changes: 6 additions & 6 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1159,9 +1159,10 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus:

// Check the header
const uint256 block_hash = block.GetHash();
CValidationState state;
if (block_hash != consensusParams.hashGenesisBlock &&
!CheckProof(block, consensusParams)) {
return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString());
!CheckProof(block, state, consensusParams)) {
return error("ReadBlockFromDisk: Errors in block header at %s: %s", pos.ToString(), FormatStateMessage(state));
}

return true;
Expand Down Expand Up @@ -3304,9 +3305,8 @@ static bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos,
static bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true)
{
// Check proof of work matches claimed amount
if (fCheckPOW && block.GetHash() != consensusParams.hashGenesisBlock
&& !CheckProof(block, consensusParams)) {
return state.DoS(50, false, REJECT_INVALID, g_signed_blocks ? "block-proof-invalid" : "high-hash", false, "proof of work failed");
if (fCheckPOW && block.GetHash() != consensusParams.hashGenesisBlock) {
return CheckProof(block, state, consensusParams);
}
return true;
}
Expand Down Expand Up @@ -3389,7 +3389,7 @@ bool IsNullDummyEnabled(const CBlockIndex* pindexPrev, const Consensus::Params&
bool IsDynaFedEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params)
{
LOCK(cs_main);
return (VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_DYNA_FED, versionbitscache) == ThresholdState::ACTIVE);
return VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_DYNA_FED, versionbitscache) == ThresholdState::ACTIVE;
}

// Compute at which vout of the block's coinbase transaction the witness
Expand Down