Skip to content

Commit 798faec

Browse files
committed
Add 'invalidateblock' and 'reconsiderblock' RPC commands.
These can be used for testing reorganizations or for manual intervention in case of chain forks.
1 parent 397b901 commit 798faec

File tree

5 files changed

+159
-0
lines changed

5 files changed

+159
-0
lines changed

src/main.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2132,6 +2132,79 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) {
21322132
return true;
21332133
}
21342134

2135+
bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) {
2136+
AssertLockHeld(cs_main);
2137+
2138+
// Mark the block itself as invalid.
2139+
pindex->nStatus |= BLOCK_FAILED_VALID;
2140+
if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex))) {
2141+
return state.Abort("Failed to update block index");
2142+
}
2143+
setBlockIndexCandidates.erase(pindex);
2144+
2145+
while (chainActive.Contains(pindex)) {
2146+
CBlockIndex *pindexWalk = chainActive.Tip();
2147+
pindexWalk->nStatus |= BLOCK_FAILED_CHILD;
2148+
if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexWalk))) {
2149+
return state.Abort("Failed to update block index");
2150+
}
2151+
setBlockIndexCandidates.erase(pindexWalk);
2152+
// ActivateBestChain considers blocks already in chainActive
2153+
// unconditionally valid already, so force disconnect away from it.
2154+
if (!DisconnectTip(state)) {
2155+
return false;
2156+
}
2157+
}
2158+
2159+
// The resulting new best tip may not be in setBlockIndexCandidates anymore, so
2160+
// add them again.
2161+
BlockMap::iterator it = mapBlockIndex.begin();
2162+
while (it != mapBlockIndex.end()) {
2163+
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && setBlockIndexCandidates.value_comp()(chainActive.Tip(), it->second)) {
2164+
setBlockIndexCandidates.insert(pindex);
2165+
}
2166+
it++;
2167+
}
2168+
2169+
InvalidChainFound(pindex);
2170+
return true;
2171+
}
2172+
2173+
bool ReconsiderBlock(CValidationState& state, CBlockIndex *pindex) {
2174+
AssertLockHeld(cs_main);
2175+
2176+
int nHeight = pindex->nHeight;
2177+
2178+
// Remove the invalidity flag from this block and all its descendants.
2179+
BlockMap::iterator it = mapBlockIndex.begin();
2180+
while (it != mapBlockIndex.end()) {
2181+
if (!it->second->IsValid() && it->second->GetAncestor(nHeight) == pindex) {
2182+
it->second->nStatus &= ~BLOCK_FAILED_MASK;
2183+
if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex))) {
2184+
return state.Abort("Failed to update block index");
2185+
}
2186+
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && setBlockIndexCandidates.value_comp()(chainActive.Tip(), it->second)) {
2187+
setBlockIndexCandidates.insert(it->second);
2188+
}
2189+
if (it->second == pindexBestInvalid) {
2190+
// Reset invalid block marker if it was pointing to one of those.
2191+
pindexBestInvalid = NULL;
2192+
}
2193+
}
2194+
it++;
2195+
}
2196+
2197+
// Remove the invalidity flag from all ancestors too.
2198+
while (pindex != NULL) {
2199+
pindex->nStatus &= ~BLOCK_FAILED_MASK;
2200+
if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex))) {
2201+
return state.Abort("Failed to update block index");
2202+
}
2203+
pindex = pindex->pprev;
2204+
}
2205+
return true;
2206+
}
2207+
21352208
CBlockIndex* AddToBlockIndex(const CBlockHeader& block)
21362209
{
21372210
// Check for duplicate

src/main.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,12 @@ class CVerifyDB {
609609
/** Find the last common block between the parameter chain and a locator. */
610610
CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator);
611611

612+
/** Mark a block as invalid. */
613+
bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex);
614+
615+
/** Remove invalidity status from a block and its descendants. */
616+
bool ReconsiderBlock(CValidationState& state, CBlockIndex *pindex);
617+
612618
/** The currently-connected chain of blocks. */
613619
extern CChain chainActive;
614620

src/rpcblockchain.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,3 +561,79 @@ Value getmempoolinfo(const Array& params, bool fHelp)
561561
return ret;
562562
}
563563

564+
Value invalidateblock(const Array& params, bool fHelp)
565+
{
566+
if (fHelp || params.size() != 1)
567+
throw runtime_error(
568+
"invalidateblock \"hash\"\n"
569+
"\nPermanently marks a block as invalid, as if it violated a consensus rule.\n"
570+
"\nArguments:\n"
571+
"1. hash (string, required) the hash of the block to mark as invalid\n"
572+
"\nResult:\n"
573+
"\nExamples:\n"
574+
+ HelpExampleCli("invalidateblock", "\"blockhash\"")
575+
+ HelpExampleRpc("invalidateblock", "\"blockhash\"")
576+
);
577+
578+
std::string strHash = params[0].get_str();
579+
uint256 hash(strHash);
580+
CValidationState state;
581+
582+
{
583+
LOCK(cs_main);
584+
if (mapBlockIndex.count(hash) == 0)
585+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
586+
587+
CBlockIndex* pblockindex = mapBlockIndex[hash];
588+
InvalidateBlock(state, pblockindex);
589+
}
590+
591+
if (state.IsValid()) {
592+
ActivateBestChain(state);
593+
}
594+
595+
if (!state.IsValid()) {
596+
throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason());
597+
}
598+
599+
return Value::null;
600+
}
601+
602+
Value reconsiderblock(const Array& params, bool fHelp)
603+
{
604+
if (fHelp || params.size() != 1)
605+
throw runtime_error(
606+
"reconsiderblock \"hash\"\n"
607+
"\nRemoves invalidity status of a block and its descendants, reconsider them for activation.\n"
608+
"This can be used to undo the effects of invalidateblock.\n"
609+
"\nArguments:\n"
610+
"1. hash (string, required) the hash of the block to reconsider\n"
611+
"\nResult:\n"
612+
"\nExamples:\n"
613+
+ HelpExampleCli("reconsiderblock", "\"blockhash\"")
614+
+ HelpExampleRpc("reconsiderblock", "\"blockhash\"")
615+
);
616+
617+
std::string strHash = params[0].get_str();
618+
uint256 hash(strHash);
619+
CValidationState state;
620+
621+
{
622+
LOCK(cs_main);
623+
if (mapBlockIndex.count(hash) == 0)
624+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
625+
626+
CBlockIndex* pblockindex = mapBlockIndex[hash];
627+
ReconsiderBlock(state, pblockindex);
628+
}
629+
630+
if (state.IsValid()) {
631+
ActivateBestChain(state);
632+
}
633+
634+
if (!state.IsValid()) {
635+
throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason());
636+
}
637+
638+
return Value::null;
639+
}

src/rpcserver.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,8 @@ static const CRPCCommand vRPCCommands[] =
270270
{ "blockchain", "gettxout", &gettxout, true, false, false },
271271
{ "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true, false, false },
272272
{ "blockchain", "verifychain", &verifychain, true, false, false },
273+
{ "blockchain", "invalidateblock", &invalidateblock, true, true, false },
274+
{ "blockchain", "reconsiderblock", &reconsiderblock, true, true, false },
273275

274276
/* Mining */
275277
{ "mining", "getblocktemplate", &getblocktemplate, true, false, false },

src/rpcserver.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,8 @@ extern json_spirit::Value gettxoutsetinfo(const json_spirit::Array& params, bool
219219
extern json_spirit::Value gettxout(const json_spirit::Array& params, bool fHelp);
220220
extern json_spirit::Value verifychain(const json_spirit::Array& params, bool fHelp);
221221
extern json_spirit::Value getchaintips(const json_spirit::Array& params, bool fHelp);
222+
extern json_spirit::Value invalidateblock(const json_spirit::Array& params, bool fHelp);
223+
extern json_spirit::Value reconsiderblock(const json_spirit::Array& params, bool fHelp);
222224

223225
// in rest.cpp
224226
extern bool HTTPReq_REST(AcceptedConnection *conn,

0 commit comments

Comments
 (0)