Anti-fork hardening + checkpoint update (v5.8.6)
Build All Platforms / test-linux-unit (push) Failing after 40s
Build All Platforms / build-linux-qt (push) Failing after 40s
Build All Platforms / build-linux-daemon (push) Failing after 40s
Build All Platforms / build-windows-qt (push) Has been cancelled
Build All Platforms / build-windows-daemon (push) Has been cancelled
Build All Platforms / build-macos (push) Has been cancelled
Build All Platforms / release (push) Has been cancelled
Build All Platforms / Trigger TRI-PI ARM64 Build (push) Has been cancelled

- Add checkpoints through block 2,209,000 to lock canonical chain
- Ban peers on incompatible forks (no common blocks after 3 getblocks)
- Auto-checkpoint: finalize blocks at MAX_REORG_DEPTH to prevent deep reorgs
- Require 10% trust delta for side-chain reorgs (first-seen advantage)
- Add gencheckpoints RPC command for easy future checkpoint generation
- Add wallet onion address to hardcoded seed list

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-22 18:14:58 -07:00
parent 1c068f4782
commit 22e220acaa
11 changed files with 223 additions and 35 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ if(POLICY CMP0167)
endif()
project(Triangles
VERSION 5.8.5
VERSION 5.8.6
DESCRIPTION "Cryptographic Triangles Wallet"
LANGUAGES C CXX
)
+8
View File
@@ -36,6 +36,10 @@ namespace Checkpoints
{2190000, uint256("0x682baf783581468ba18f9967254a7f3944e8b8c4cc7101e7d99b68f4f9dd5271")},
{2200000, uint256("0x0a8d0442f031f1258120f713f34e45f4f9a625fb753558e27b89b32ad5a9a740")},
{2205000, uint256("0xf7aa893ec012181e321783d6a5487addf6997377908faa3e760c9054a4217d29")},
{2206000, uint256("0x780ae878f8b10b6cbd51ceb2c0799c90d3551fa916be62974375071b9e36581c")},
{2207000, uint256("0x8836d67b0f08036c4a7c26ff0a29d4461a52b6d8f552165ad9c1abec2f3cadfd")},
{2208000, uint256("0xe4a19e8a29fa7aae47f7563377af3e896fee18ed069a64e325b8c2c6c820a1be")},
{2209000, uint256("0x04c78a6fc863bed918a9364c58c64489943b2e85d84ddb1ac2fba584f390d5dc")},
};
static MapCheckpoints mapCheckpointsTestnet = {
@@ -55,6 +59,10 @@ namespace Checkpoints
{2190000, uint256("0x682baf783581468ba18f9967254a7f3944e8b8c4cc7101e7d99b68f4f9dd5271")},
{2200000, uint256("0x0a8d0442f031f1258120f713f34e45f4f9a625fb753558e27b89b32ad5a9a740")},
{2205000, uint256("0xf7aa893ec012181e321783d6a5487addf6997377908faa3e760c9054a4217d29")},
{2206000, uint256("0x780ae878f8b10b6cbd51ceb2c0799c90d3551fa916be62974375071b9e36581c")},
{2207000, uint256("0x8836d67b0f08036c4a7c26ff0a29d4461a52b6d8f552165ad9c1abec2f3cadfd")},
{2208000, uint256("0xe4a19e8a29fa7aae47f7563377af3e896fee18ed069a64e325b8c2c6c820a1be")},
{2209000, uint256("0x04c78a6fc863bed918a9364c58c64489943b2e85d84ddb1ac2fba584f390d5dc")},
};
bool CheckHardened(int nHeight, const uint256& hash)
+1 -1
View File
@@ -8,7 +8,7 @@
// These need to be macros, as version.cpp's and triangles-qt.rc's voodoo requires it
#define CLIENT_VERSION_MAJOR 5
#define CLIENT_VERSION_MINOR 8
#define CLIENT_VERSION_REVISION 5
#define CLIENT_VERSION_REVISION 6
#define CLIENT_VERSION_BUILD 0
// Converts the parameter X to a string after macro replacement on X has been performed.
+93 -11
View File
@@ -71,6 +71,7 @@ uint256 nBestInvalidTrust = 0;
uint256 hashBestChain = 0;
CBlockIndex* pindexBest = NULL;
CBlockIndex* pindexFinalized = NULL; // auto-checkpoint: deepest finalized block
bool fAddressIndex = false;
int64_t nTimeBestReceived = 0;
@@ -2506,15 +2507,25 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
return error("Reorganize() : pfork->pprev is null");
}
// Finality: reject reorgs deeper than MAX_REORG_DEPTH blocks.
// This prevents long-range attacks on the PoS chain. During IBD
// we allow deep reorgs since we haven't settled on a tip yet.
unsigned int nDisconnectDepth = pindexBest->nHeight - pfork->nHeight;
if (!IsInitialBlockDownload() && nDisconnectDepth > MAX_REORG_DEPTH)
// Finality: reject reorgs that go below the auto-checkpoint or
// exceed MAX_REORG_DEPTH blocks. During IBD we allow deep reorgs
// since we haven't settled on a tip yet.
if (!IsInitialBlockDownload())
{
printf("REORGANIZE: REJECTED — depth %u exceeds finality limit %u (fork at %d)\n",
nDisconnectDepth, MAX_REORG_DEPTH, pfork->nHeight);
return error("Reorganize() : reorg depth %u exceeds maximum %u", nDisconnectDepth, MAX_REORG_DEPTH);
if (pindexFinalized && pfork->nHeight < pindexFinalized->nHeight)
{
printf("REORGANIZE: REJECTED — fork at %d is below finalized block %d\n",
pfork->nHeight, pindexFinalized->nHeight);
return error("Reorganize() : fork point %d below auto-checkpoint %d",
pfork->nHeight, pindexFinalized->nHeight);
}
unsigned int nDisconnectDepth = pindexBest->nHeight - pfork->nHeight;
if (nDisconnectDepth > MAX_REORG_DEPTH)
{
printf("REORGANIZE: REJECTED — depth %u exceeds finality limit %u (fork at %d)\n",
nDisconnectDepth, MAX_REORG_DEPTH, pfork->nHeight);
return error("Reorganize() : reorg depth %u exceeds maximum %u", nDisconnectDepth, MAX_REORG_DEPTH);
}
}
// List of what to disconnect
@@ -2739,6 +2750,23 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
nTimeBestReceived = GetTime();
nTransactionsUpdated++;
// Auto-checkpoint: finalize the block at depth MAX_REORG_DEPTH.
// Only set when fully synced (not IBD) so we don't lock in a
// potentially wrong chain during initial sync.
if (!IsInitialBlockDownload() && nBestHeight > (int)MAX_REORG_DEPTH)
{
CBlockIndex* pcandidate = pindexBest;
for (int i = 0; i < (int)MAX_REORG_DEPTH && pcandidate; i++)
pcandidate = pcandidate->pprev;
if (pcandidate && pcandidate != pindexFinalized)
{
pindexFinalized = pcandidate;
printf("AUTO-CHECKPOINT: block %d (%s) is now finalized\n",
pindexFinalized->nHeight,
pindexFinalized->GetBlockHash().ToString().substr(0,20).c_str());
}
}
uint256 nBestBlockTrust = (pindexBest->nHeight != 0 && pindexBest->pprev) ? (pindexBest->nChainTrust - pindexBest->pprev->nChainTrust) : pindexBest->nChainTrust;
// Log every 5000 blocks during sync, every block once caught up
@@ -3004,8 +3032,12 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos, const u
// writes to the same transaction, cutting the per-block commit count in half.
//
// Chain selection rules:
// 1. Strictly greater trust always wins (normal case).
// 2. Equal trust: deterministic tiebreaker with timestamp preference.
// 1. Linear extension: always accept (no reorg needed).
// 2. Side-chain reorg: require 10% more cumulative trust than current
// best chain. This gives a strong "first-seen" advantage and
// prevents endless fork-thrashing on a small network.
// During IBD the delta is waived so the heaviest chain wins.
// 3. Equal trust: deterministic tiebreaker with timestamp preference.
// First prefer the block with the earlier timestamp (lower nTime),
// then break remaining ties by lower hash. This converges faster
// because the earlier block is more likely to have propagated first.
@@ -3013,7 +3045,33 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos, const u
bool fNewBest = false;
static int64_t nLastEqualTrustReorg = 0;
if (pindexNew->nChainTrust > nBestChainTrust)
fNewBest = true;
{
bool fLinearExtension = (pindexNew->pprev == pindexBest);
if (fLinearExtension || IsInitialBlockDownload())
{
fNewBest = true;
}
else
{
// Side-chain reorg: require 10% more trust.
// new * 10 > best * 11 ⟺ new > best * 1.1
CBigNum bnNewTrust(pindexNew->nChainTrust);
CBigNum bnBestTrust(nBestChainTrust);
if (bnNewTrust * 10 > bnBestTrust * 11)
{
fNewBest = true;
printf("CHAIN: Side-chain reorg accepted (trust delta sufficient)\n");
}
else
{
printf("CHAIN: Side-chain at height %d REJECTED — insufficient trust delta "
"(need >10%% more, have %s vs %s)\n",
pindexNew->nHeight,
bnNewTrust.ToString().c_str(),
bnBestTrust.ToString().c_str());
}
}
}
else if (pindexNew->nChainTrust == nBestChainTrust && pindexBest &&
GetTime() - nLastEqualTrustReorg > 2 * 60)
{
@@ -4591,6 +4649,30 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
// Find the last block the caller has in the main chain
CBlockIndex* pindex = locator.GetBlockIndex();
// Detect incompatible fork: peer sent a locator with entries but
// GetBlockIndex() fell through to genesis (no locator hash matched
// our main chain). If the peer's tip isn't our genesis,
// they're on a completely different fork.
if (!locator.IsNull() && pindex == pindexGenesisBlock &&
pindexGenesisBlock && locator.GetTipHash() != pindexGenesisBlock->GetBlockHash())
{
pfrom->nIncompatibleGetblocks++;
if (pfrom->nIncompatibleGetblocks >= 3)
{
printf("WARNING: peer %s sent %d getblocks with no common blocks — disconnecting (incompatible fork)\n",
pfrom->addr.ToString().c_str(), pfrom->nIncompatibleGetblocks);
pfrom->Misbehaving(100);
return true;
}
printf("WARNING: peer %s getblocks locator has no common blocks (%d/3 before ban)\n",
pfrom->addr.ToString().c_str(), pfrom->nIncompatibleGetblocks);
}
else if (pindex && pindex != pindexGenesisBlock)
{
// Peer matched a non-genesis block — they share our chain
pfrom->nIncompatibleGetblocks = 0;
}
// Send the rest of the chain
if (pindex)
pindex = pindex->pnext;
+8 -1
View File
@@ -39,7 +39,7 @@ static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50;
static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100;
static const unsigned int MAX_ORPHAN_BLOCKS = 2000;
static const unsigned int MAX_ORPHAN_BLOCKS_IBD = 4000;
static const unsigned int MAX_REORG_DEPTH = 500; // reject reorgs deeper than this (finality)
static const unsigned int MAX_REORG_DEPTH = 100; // reject reorgs deeper than this (finality)
static const unsigned int MAX_INV_SZ = 50000;
static const int64_t MIN_TX_FEE = (1 * CENT) / 100;
static const int64_t MIN_RELAY_TX_FEE = (1 * CENT) / 100;
@@ -84,6 +84,7 @@ extern uint256 nBestChainTrust;
extern uint256 nBestInvalidTrust;
extern uint256 hashBestChain;
extern CBlockIndex* pindexBest;
extern CBlockIndex* pindexFinalized; // auto-checkpoint: deepest finalized block
extern unsigned int nTransactionsUpdated;
extern uint64_t nLastBlockTx;
extern uint64_t nLastBlockSize;
@@ -1561,6 +1562,12 @@ public:
return vHave.empty();
}
// Return the first hash in the locator (peer's tip), or 0 if empty
uint256 GetTipHash() const
{
return vHave.empty() ? uint256(0) : vHave[0];
}
void Set(const CBlockIndex* pindex)
{
vHave.clear();
+65 -21
View File
@@ -47,7 +47,7 @@ void ThreadOpenAddedConnections2(void* parg);
void ThreadMapPort2(void* parg);
#endif
void ThreadHTTPSeedFetch(void* parg);
void ThreadHTTPSeedFetch2(void* parg);
bool ThreadHTTPSeedFetch2(void* parg);
bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false);
@@ -1381,7 +1381,7 @@ void ThreadOnionSeed(void* parg)
// Load hardcoded .onion seeds (if any)
// Load hardcoded .onion seeds and queue them for immediate direct connection
static const char *(*strOnionSeed)[1] = fTestNet ? strTestNetOnionSeed : strMainNetOnionSeed;
int found = 0;
@@ -1394,23 +1394,50 @@ void ThreadOnionSeed(void* parg)
CAddress addr = CAddress(CService(parsed, GetDefaultPort()));
addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay);
addrman.Add(addr, parsed);
// Queue for immediate direct connection (OneShot) — don't wait for
// addrman selection which deprioritizes stale timestamps
std::string oneShotAddr = std::string(strOnionSeed[seed_idx][0])
+ ":" + std::to_string(GetDefaultPort());
AddOneShot(oneShotAddr);
found++;
}
printf("%d addresses from hardcoded .onion seeds\n", found);
printf("%d addresses from hardcoded .onion seeds (queued as OneShot)\n", found);
// Also fetch dynamic seeds from HTTP seed list
// Wait for Tor to establish circuits before attempting HTTPS seed fetch.
// The hardcoded OneShot connections can race ahead meanwhile.
printf("ThreadOnionSeed: waiting 20s for Tor circuits before HTTPS seed fetch...\n");
for (int i = 0; i < 20 && !fShutdown; i++)
MilliSleep(1000);
// Fetch dynamic seeds with retry — up to 4 attempts with increasing backoff.
// This is the primary discovery mechanism — seeds.cryptographic-triangles.org
ThreadHTTPSeedFetch2(NULL);
{
bool ok = false;
int delays[] = {0, 30, 60, 120};
for (int attempt = 0; attempt < 4 && !ok && !fShutdown; attempt++) {
if (attempt > 0) {
printf("ThreadOnionSeed: HTTPS seed fetch retry %d in %ds...\n", attempt, delays[attempt]);
for (int i = 0; i < delays[attempt] && !fShutdown; i++)
MilliSleep(1000);
}
if (!fShutdown)
ok = ThreadHTTPSeedFetch2(NULL);
}
if (!ok && !fShutdown)
printf("ThreadOnionSeed: all HTTPS seed fetch attempts failed\n");
}
printf("ThreadOnionSeed: initial seeding complete\n");
// Periodic re-seeding: if the node becomes isolated (0 outbound peers),
// re-fetch the seed list. Check every 10 minutes, re-seed at most once
// per 30 minutes to avoid hammering the seed server.
// Periodic re-seeding for isolated or under-connected nodes.
// Check every 2 minutes, re-seed when < 2 outbound peers.
// First re-seed after 5 min cooldown, then 15 min for subsequent.
int64_t nLastReseed = GetTime();
bool bFirstReseed = true;
while (!fShutdown) {
for (int i = 0; i < 600 && !fShutdown; i++) // sleep 10 minutes
for (int i = 0; i < 120 && !fShutdown; i++) // sleep 2 minutes
MilliSleep(1000);
if (fShutdown) break;
@@ -1423,10 +1450,20 @@ void ThreadOnionSeed(void* parg)
nOutbound++;
}
if (nOutbound == 0 && GetTime() - nLastReseed > 30 * 60) {
printf("ThreadOnionSeed: no outbound peers, re-seeding...\n");
int64_t nCooldown = bFirstReseed ? 5 * 60 : 15 * 60;
if (nOutbound < 2 && GetTime() - nLastReseed > nCooldown) {
printf("ThreadOnionSeed: low outbound peers (%d), re-seeding...\n", nOutbound);
ThreadHTTPSeedFetch2(NULL);
// Also re-queue hardcoded seeds for direct connection
for (unsigned int seed_idx = 0; strOnionSeed[seed_idx][0] != NULL; seed_idx++) {
std::string oneShotAddr = std::string(strOnionSeed[seed_idx][0])
+ ":" + std::to_string(GetDefaultPort());
AddOneShot(oneShotAddr);
}
nLastReseed = GetTime();
bFirstReseed = false;
}
}
}
@@ -1486,7 +1523,7 @@ void ThreadDumpAddress(void* parg)
printf("ThreadDumpAddress exited\n");
}
void ThreadHTTPSeedFetch2(void* parg)
bool ThreadHTTPSeedFetch2(void* parg)
{
static const char* DEFAULT_SEED_URL_HOST = "seeds.cryptographic-triangles.org";
static const char* DEFAULT_SEED_URL_PATH = "/seeds.txt";
@@ -1515,7 +1552,7 @@ void ThreadHTTPSeedFetch2(void* parg)
if (!ConnectSocketByName(addrResolved, hSocket, connectDest.c_str(), HTTPS_PORT, nConnectTimeout)) {
printf("HTTPS seed fetch: cannot connect to %s through Tor proxy\n", seedHost.c_str());
return;
return false;
}
// Set up TLS over the connected socket
@@ -1523,7 +1560,7 @@ void ThreadHTTPSeedFetch2(void* parg)
if (!ctx) {
printf("HTTPS seed fetch: SSL_CTX_new failed\n");
closesocket(hSocket);
return;
return false;
}
// Use system default CA certificates for verification
@@ -1535,7 +1572,7 @@ void ThreadHTTPSeedFetch2(void* parg)
printf("HTTPS seed fetch: SSL_new failed\n");
SSL_CTX_free(ctx);
closesocket(hSocket);
return;
return false;
}
// Set SNI hostname (required for Caddy/Let's Encrypt)
@@ -1552,7 +1589,7 @@ void ThreadHTTPSeedFetch2(void* parg)
SSL_free(ssl);
SSL_CTX_free(ctx);
closesocket(hSocket);
return;
return false;
}
printf("HTTPS seed fetch: TLS connection established to %s\n", seedHost.c_str());
@@ -1575,7 +1612,7 @@ void ThreadHTTPSeedFetch2(void* parg)
SSL_free(ssl);
SSL_CTX_free(ctx);
closesocket(hSocket);
return;
return false;
}
nSent += nBytes;
}
@@ -1600,21 +1637,21 @@ void ThreadHTTPSeedFetch2(void* parg)
if (response.empty()) {
printf("HTTPS seed fetch: empty response from %s\n", seedHost.c_str());
return;
return false;
}
// Parse HTTP response - find end of headers
size_t headerEnd = response.find("\r\n\r\n");
if (headerEnd == std::string::npos) {
printf("HTTPS seed fetch: malformed response (no header terminator)\n");
return;
return false;
}
// Check status code
std::string statusLine = response.substr(0, response.find("\r\n"));
if (statusLine.find("200") == std::string::npos) {
printf("HTTPS seed fetch: %s from %s\n", statusLine.c_str(), seedHost.c_str());
return;
return false;
}
std::string body = response.substr(headerEnd + 4);
@@ -1626,7 +1663,7 @@ void ThreadHTTPSeedFetch2(void* parg)
while (std::getline(lines, line))
{
if (fShutdown)
return;
return false;
// Trim whitespace and carriage returns
while (!line.empty() && (line.back() == '\r' || line.back() == ' ' || line.back() == '\t'))
@@ -1667,17 +1704,24 @@ void ThreadHTTPSeedFetch2(void* parg)
CAddress addr(CService(parsed, port));
addr.nTime = GetTime() - 3*24*60*60; // 3 days ago
addrman.Add(addr, CNetAddr("https-seed", true));
// Queue the first 8 seeds for immediate direct connection
if (found < 8) {
std::string oneShotAddr = addrStr + ":" + std::to_string(port);
AddOneShot(oneShotAddr);
}
found++;
}
}
printf("%d addresses found from HTTPS seed list (%s)\n", found, seedHost.c_str());
return found > 0;
} catch (std::exception& e) {
printf("HTTPS seed fetch failed: %s\n", e.what());
if (ssl) { SSL_shutdown(ssl); SSL_free(ssl); }
if (ctx) SSL_CTX_free(ctx);
if (hSocket != INVALID_SOCKET) closesocket(hSocket);
return false;
}
}
+2
View File
@@ -275,6 +275,7 @@ public:
int64_t nLastTipCheck; // last time we asked this peer for chain tip
int64_t nAvgBlockLatencyUs; // rolling average block delivery latency (microseconds)
int nBlocksDelivered; // count of blocks delivered by this peer
int nIncompatibleGetblocks; // count of getblocks with no common blocks (fork detection)
// flood relay
std::vector<CAddress> vAddrToSend;
@@ -325,6 +326,7 @@ public:
nLastTipCheck = 0;
nAvgBlockLatencyUs = 0;
nBlocksDelivered = 0;
nIncompatibleGetblocks = 0;
fGetAddr = false;
nMisbehavior = 0;
hashCheckpointKnown = 0;
+1
View File
@@ -5,6 +5,7 @@
// Hardcoded onion seed nodes for initial peer discovery.
// Also fetched dynamically via https://seeds.cryptographic-triangles.org/seeds.txt
static const char *strMainNetOnionSeed[][1] = {
{"6ygpphp2qsucwvhwefv6h6ehvk6zjf7b7zdp4ggkzjjwe76cg6jwm7id.onion"},
{"jbpfhe7zw3qm67wy3j2ayysp3mnrjobopthnko3b3sgahqtecblwqmid.onion"},
{"uddaxjbo3lh2zskg7w6gwln4ty5cel7q4c5jbx7fdtv6zf2j47gdlyad.onion"},
{"el5sirhhleecuctpeeprelzubpqmoqivvra3rzlwbjttinxa4fq3wnid.onion"},
+42
View File
@@ -462,6 +462,48 @@ Value getblockchaininfo(const Array& params, bool fHelp)
return obj;
}
Value gencheckpoints(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 1)
throw runtime_error(
"gencheckpoints [interval]\n"
"Generates hardcoded checkpoint entries for checkpoints.cpp.\n"
"Outputs C++ map entries for every <interval> blocks (default 5000)\n"
"from genesis to current tip, ready to paste into the source code.");
int nInterval = 5000;
if (params.size() > 0)
nInterval = params[0].get_int();
if (nInterval < 1)
throw runtime_error("Interval must be >= 1");
std::string result;
result += "// Generated by gencheckpoints RPC at height " + std::to_string(nBestHeight) + "\n";
result += "static MapCheckpoints mapCheckpoints = {\n";
// Always include genesis
CBlockIndex* pindex = mapBlockIndex[hashBestChain];
while (pindex->pprev)
pindex = pindex->pprev;
bool first = true;
while (pindex)
{
if (pindex->nHeight % nInterval == 0 || pindex->nHeight == nBestHeight)
{
if (!first)
result += ",\n";
result += " {" + std::to_string(pindex->nHeight) + ", uint256(\"0x"
+ pindex->GetBlockHash().GetHex() + "\")}";
first = false;
}
pindex = pindex->pnext;
}
result += "\n};\n";
return result;
}
// ============================================================================
// Address index RPC commands
// ============================================================================
+1
View File
@@ -314,6 +314,7 @@ static const CRPCCommand vRPCCommands[] =
{ "signrawtransaction", &signrawtransaction, false, false },
{ "sendrawtransaction", &sendrawtransaction, false, false },
{ "getcheckpoint", &getcheckpoint, true, false },
{ "gencheckpoints", &gencheckpoints, true, false },
{ "getchaintips", &getchaintips, true, false },
{ "invalidateblock", &invalidateblock, false, false },
{ "reconsiderblock", &reconsiderblock, false, false },
+1
View File
@@ -222,6 +222,7 @@ extern json_spirit::Value getblockhash(const json_spirit::Array& params, bool fH
extern json_spirit::Value getblock(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getblockbynumber(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getcheckpoint(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value gencheckpoints(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getchaintips(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value invalidateblock(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value reconsiderblock(const json_spirit::Array& params, bool fHelp);