diff --git a/CMakeLists.txt b/CMakeLists.txt index 3be94dc..f69bb44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 ) diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index 43b4e44..5de2f73 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -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) diff --git a/src/clientversion.h b/src/clientversion.h index ef08e74..fbba817 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -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. diff --git a/src/main.cpp b/src/main.cpp index 9eccc95..658ea29 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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; diff --git a/src/main.h b/src/main.h index 16931a9..8c4804e 100644 --- a/src/main.h +++ b/src/main.h @@ -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(); diff --git a/src/net.cpp b/src/net.cpp index f827510..4dfa248 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -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; } } diff --git a/src/net.h b/src/net.h index 6ca5f98..ac80e1d 100644 --- a/src/net.h +++ b/src/net.h @@ -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 vAddrToSend; @@ -325,6 +326,7 @@ public: nLastTipCheck = 0; nAvgBlockLatencyUs = 0; nBlocksDelivered = 0; + nIncompatibleGetblocks = 0; fGetAddr = false; nMisbehavior = 0; hashCheckpointKnown = 0; diff --git a/src/onionseed.h b/src/onionseed.h index eabb587..5eba366 100644 --- a/src/onionseed.h +++ b/src/onionseed.h @@ -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"}, diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 2258685..c6a4c48 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -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 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 // ============================================================================ diff --git a/src/trianglesrpc.cpp b/src/trianglesrpc.cpp index 0701ac9..ceac2b1 100644 --- a/src/trianglesrpc.cpp +++ b/src/trianglesrpc.cpp @@ -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 }, diff --git a/src/trianglesrpc.h b/src/trianglesrpc.h index 2ec8d80..8c0b9ad 100644 --- a/src/trianglesrpc.h +++ b/src/trianglesrpc.h @@ -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);