Skip to content
Closed
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
2 changes: 2 additions & 0 deletions contrib/devtools/check-doc.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

SET_DOC_OPTIONAL.update(['-con_fpowallowmindifficultyblocks', '-con_fpownoretargeting', '-con_nsubsidyhalvinginterval', '-con_bip34height', '-con_bip65height', '-con_bip66height', '-con_npowtargettimespan', '-con_npowtargetspacing', '-con_nrulechangeactivationthreshold', '-con_nminerconfirmationwindow', '-con_powlimit', '-con_parentpowlimit', '-con_bip34hash', '-con_nminimumchainwork', '-con_defaultassumevalid', '-parentgenesisblockhash', '-ndefaultport', '-npruneafterheight', '-fdefaultconsistencychecks', '-frequirestandard', '-fmineblocksondemand', '-mainchainrpccookiefile', '-testnet', '-ct_bits', '-ct_exponent', '-anyonecanspendaremine', '-fminingrequirespeers', '-fmineblocksondemand', '-con_mandatorycoinbase', '-con_has_parent_chain'])

SET_DOC_OPTIONAL.update(['-testemergencymode', '-ignoreemergencymode'])

def main():
used = check_output(CMD_GREP_ARGS, shell=True)
docd = check_output(CMD_GREP_DOCS, shell=True)
Expand Down
1 change: 1 addition & 0 deletions qa/pull-tester/rpc-tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
'feature_fedpeg.py',
'default_asset_name.py',
'assetdir.py',
'emergency.py',

# Elements' specially adapted tests second
'blockchain.py',
Expand Down
63 changes: 63 additions & 0 deletions qa/rpc-tests/emergency.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env python3
# Copyright (c) 2018-2018 The Elements Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.

from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
bitcoind_processes,
start_nodes,
start_node,
stop_nodes,
)

import os
import tempfile
import time

class EmergencyModeTest(BitcoinTestFramework):

def __init__(self):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 1
# Both -testemergencymode and -ignoreemergencymode shouldn't do anything
self.extra_args = [['-debug', '-testemergencymode', '-ignoreemergencymode']]

def setup_network(self):
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args)

def run_test(self):

print("Ensure that node is not already in emergency mode.")
emergency_file_path = self.options.tmpdir + "/node0/elementsregtest/ERROR_elementsregtest_HAS_SUFFERED_A_CRITICAL_FAILURE_AND_MAY_BE_UNSAFE_CORRECT_ERROR_BEFORE_REMOVING_THIS_FILE"
assert(not os.path.isfile(emergency_file_path))

self.nodes[0].generate(1) # Just to double-check the node works
stop_nodes(self.nodes)

self.extra_args = [['-debug', '-testemergencymode']]
log_stderr = tempfile.SpooledTemporaryFile(max_size=2**16)
try:
self.nodes[0] = start_node(0, self.options.tmpdir, self.extra_args[0], stderr=log_stderr)
raise Exception("Node shouldn't start correctly with -testemergencymode")
except Exception as e:
return_code = bitcoind_processes[0].wait()
assert(return_code == 1)
assert(str(e) == "bitcoind exited with status 1 during initialization")
assert(os.path.isfile(emergency_file_path))
log_stderr.seek(0)
stderr_out = log_stderr.read().decode('utf-8')
assert(stderr_out == 'Error: Using -testemergencymode\n'
'Error: Error: A fatal internal error occurred, see debug.log for details\n')
log_stderr.close()

os.remove(emergency_file_path)
# Once the file is removed it starts normally again
self.extra_args = [['-debug']]
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args)

print("Success!")

if __name__ == '__main__':
EmergencyModeTest().main()
4 changes: 2 additions & 2 deletions qa/rpc-tests/test_framework/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ def _rpchost_to_args(rpchost):
rv += ['-rpcport=' + rpcport]
return rv

def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None, chain='elementsregtest'):
def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None, chain='elementsregtest', stderr=sys.stderr):
"""
Start a bitcoind and return RPC connection to it
"""
Expand All @@ -342,7 +342,7 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=
binary = os.getenv("ELEMENTSD", "elementsd")
args = [ binary, '-chain='+chain, "-datadir="+datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-mocktime="+str(get_mocktime()) ]
if extra_args is not None: args.extend(extra_args)
bitcoind_processes[i] = subprocess.Popen(args)
bitcoind_processes[i] = subprocess.Popen(args, stderr=stderr)
if os.getenv("PYTHON_DEBUG", ""):
print("start_node: bitcoind started, waiting for RPC to come up")
url = rpc_url(i, rpchost)
Expand Down
5 changes: 5 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,11 @@ bool AppInitParameterInteraction()
{
const CChainParams& chainparams = Params();
// ********************************************************* Step 2: parameter interactions
CValidationState state;
if (GetBoolArg("-testemergencymode", false)) {
SetEmergencyMode(state, chainparams, "Testing emergency mode, the node should abort.", "Using -testemergencymode");
}
InitEmergencyMode(chainparams);

// also see: InitParameterInteraction()

Expand Down
76 changes: 68 additions & 8 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2053,6 +2053,45 @@ bool AbortNode(CValidationState& state, const std::string& strMessage, const std

} // anon namespace

static const std::string EMERGENCY_MODE_FILE_NAME = "_HAS_SUFFERED_A_CRITICAL_FAILURE_AND_MAY_BE_UNSAFE_CORRECT_ERROR_BEFORE_REMOVING_THIS_FILE";

void SetEmergencyMode(CValidationState& state, const CChainParams& chainparams, const std::string& strMessage, const std::string& userMessage)
{
if (GetBoolArg("-ignoreemergencymode", false)) {
return;
}
const std::string filename = "ERROR_" + chainparams.NetworkIDString() + EMERGENCY_MODE_FILE_NAME;
boost::filesystem::path path = GetDataDir() / filename;
std::ofstream out(path.string().c_str());
if (out) {
AbortNode(state, strMessage, userMessage);
} else {
AbortNode(state, "WITHOUT WRITTING EMERGENCY FILE:" + strMessage, userMessage);
}
}

void InitEmergencyMode(const CChainParams& chainparams)
{
if (GetBoolArg("-ignoreemergencymode", false)) {
return;
}
const std::string filename = "ERROR_" + chainparams.NetworkIDString() + EMERGENCY_MODE_FILE_NAME;
CValidationState state;
const std::string& strMessage = strprintf("%s(): %s", __func__, filename);
if (boost::filesystem::exists(GetDataDir() / filename)) {
AbortNode(state, strMessage);
}
}

static void CheckInvalidBlockSignatures(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams)
{
const CScript defaultRegtestScript(CScript() << OP_TRUE);
if (defaultRegtestScript != block.proof.challenge && CheckProof(block, chainparams.GetConsensus())) {
SetEmergencyMode(state, chainparams, "Block signers are signing invalid blocks",
"Block signers are signing invalid blocks: " + state.GetRejectReason() + " " + state.GetDebugMessage());
}
}

/**
* Apply the undo operation of a CTxInUndo to the given chain state.
* @param undo The undo object.
Expand Down Expand Up @@ -3215,6 +3254,12 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
vinvalidBlocks.push_back(blockConnecting.GetHash());
pblocktree->WriteInvalidBlockQueue(vinvalidBlocks);
}
} else {
// If validation fails due to REJECT_PEGIN, It could simply be the case that the parent daemon
// is not in sync and therefore claimpegin transactions can not be validated.
// This check can not occur later in the validation path (f.e. after ActivateBestChain)
// because the validation state is reset hereafter.
CheckInvalidBlockSignatures(blockConnecting, state, chainparams);
}
}

Expand Down Expand Up @@ -3777,7 +3822,7 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne
bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW)
{
// Check proof of work matches claimed amount
if (fCheckPOW && !CheckProof(block, Params().GetConsensus()))
if (fCheckPOW && !CheckProof(block, consensusParams))
return state.DoS(50, error("CheckBlockHeader(): block proof invalid"),
REJECT_INVALID, "block-proof-invalid", true);

Expand Down Expand Up @@ -4211,10 +4256,13 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
if (ret) {
// Store to disk
ret = AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, NULL, fNewBlock);
} else {
CheckInvalidBlockSignatures(*pblock, state, chainparams);
}
CheckBlockIndex(chainparams.GetConsensus());
if (!ret) {
GetMainSignals().BlockChecked(*pblock, state);
CheckInvalidBlockSignatures(*pblock, state, chainparams);
return error("%s: AcceptBlock FAILED", __func__);
}
}
Expand All @@ -4241,14 +4289,22 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
indexDummy.nHeight = pindexPrev->nHeight + 1;

// NOTE: CheckBlockHeader is called by CheckBlock
if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev, GetAdjustedTime()))
if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev, GetAdjustedTime())) {
CheckInvalidBlockSignatures(block, state, chainparams);
return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, FormatStateMessage(state));
if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot))
}
if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot)) {
CheckInvalidBlockSignatures(block, state, chainparams);
return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state));
if (!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindexPrev))
}
if (!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindexPrev)) {
CheckInvalidBlockSignatures(block, state, chainparams);
return error("%s: Consensus::ContextualCheckBlock: %s", __func__, FormatStateMessage(state));
if (!ConnectBlock(block, state, &indexDummy, viewNew, chainparams, NULL, true))
return false;
}
if (!ConnectBlock(block, state, &indexDummy, viewNew, chainparams, NULL, true)) {
CheckInvalidBlockSignatures(block, state, chainparams);
return error("%s: ConnectBlock: %s", __func__, FormatStateMessage(state));
}
assert(state.IsValid());

return true;
Expand Down Expand Up @@ -4617,9 +4673,11 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()))
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
// check level 1: verify block validity
if (nCheckLevel >= 1 && !CheckBlock(block, state, chainparams.GetConsensus()))
if (nCheckLevel >= 1 && !CheckBlock(block, state, chainparams.GetConsensus())) {
CheckInvalidBlockSignatures(block, state, chainparams);
return error("%s: *** found bad block at %d, hash=%s (%s)\n", __func__,
pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state));
}
// check level 2: verify undo validity
if (nCheckLevel >= 2 && pindex) {
CBlockUndo undo;
Expand Down Expand Up @@ -4657,8 +4715,10 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
CBlock block;
if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()))
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
if (!ConnectBlock(block, state, pindex, coins, chainparams))
if (!ConnectBlock(block, state, pindex, coins, chainparams)) {
CheckInvalidBlockSignatures(block, state, chainparams);
return error("VerifyDB(): *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
}
}
}

Expand Down
10 changes: 10 additions & 0 deletions src/validation.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,16 @@ static const unsigned int DEFAULT_CHECKLEVEL = 3;
// Setting the target to > than 550MB will make it likely we can respect the target.
static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024;

/**
* Create the emergency file and aborts the node.
*/
void SetEmergencyMode(CValidationState& state, const CChainParams& chainparams, const std::string& strMessage, const std::string& userMessage="");
/**
* Checks if the emergency file exists and if so aborts the node. This
* is intended to be used on initialization.
*/
void InitEmergencyMode(const CChainParams& chainparams);

/**
* Process an incoming block. This only returns after the best known valid
* block is made active. Note that it does not, however, guarantee that the
Expand Down