diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 3da94fbf7..ecb720043 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -1,489 +1,491 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Copyright (c) 2017-2018 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include static CBlock CreateGenesisBlock(const char *pszTimestamp, const CScript &genesisOutputScript, uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const Amount genesisReward) { CMutableTransaction txNew; txNew.nVersion = 1; txNew.vin.resize(1); txNew.vout.resize(1); txNew.vin[0].scriptSig = CScript() << 486604799 << CScriptNum(4) << std::vector((const uint8_t *)pszTimestamp, (const uint8_t *)pszTimestamp + strlen(pszTimestamp)); txNew.vout[0].nValue = genesisReward; txNew.vout[0].scriptPubKey = genesisOutputScript; CBlock genesis; genesis.nTime = nTime; genesis.nBits = nBits; genesis.nNonce = nNonce; genesis.nVersion = nVersion; genesis.vtx.push_back(MakeTransactionRef(std::move(txNew))); genesis.hashPrevBlock.SetNull(); genesis.hashMerkleRoot = BlockMerkleRoot(genesis); return genesis; } /** * Build the genesis block. Note that the output of its generation transaction * cannot be spent since it did not originally exist in the database. * * CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000, * hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893, * vtx=1) * CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0) * CTxIn(COutPoint(000000, -1), coinbase * 04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73) * CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B) * vMerkleTree: 4a5e1e */ CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const Amount genesisReward) { const char *pszTimestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"; const CScript genesisOutputScript = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909" "a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112" "de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG; return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTime, nNonce, nBits, nVersion, genesisReward); } /** * Main network */ /** * What makes a good checkpoint block? * + Is surrounded by blocks with reasonable timestamps * (no blocks before with a timestamp after, none after with * timestamp before) * + Contains no strange transactions */ class CMainParams : public CChainParams { public: CMainParams() { strNetworkID = "main"; consensus.nSubsidyHalvingInterval = 210000; // 00000000000000ce80a7e057163a4db1d5ad7b20fb6f598c9597b9665c8fb0d4 - // April 1, 2012 consensus.BIP16Height = 173805; consensus.BIP34Height = 227931; consensus.BIP34Hash = uint256S( "000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8"); // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0 consensus.BIP65Height = 388381; // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931 consensus.BIP66Height = 363725; // 000000000000000004a1b34462cb8aeebd5799177f7a29cf28f2d1961716b5b5 consensus.CSVHeight = 419328; consensus.powLimit = uint256S( "00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // two weeks consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; consensus.nPowTargetSpacing = 10 * 60; consensus.fPowAllowMinDifficultyBlocks = false; consensus.fPowNoRetargeting = false; // The best chain should have at least this much work. consensus.nMinimumChainWork = ChainParamsConstants::MAINNET_MINIMUM_CHAIN_WORK; // By default assume that the signatures in ancestors of this block are // valid. consensus.defaultAssumeValid = ChainParamsConstants::MAINNET_DEFAULT_ASSUME_VALID; // August 1, 2017 hard fork consensus.uahfHeight = 478558; // November 13, 2017 hard fork consensus.daaHeight = 504031; // November 15, 2018 hard fork consensus.magneticAnomalyHeight = 556766; // Nov 15, 2019 12:00:00 UTC protocol upgrade consensus.gravitonActivationTime = 1573819200; // May 15, 2020 12:00:00 UTC protocol upgrade consensus.phononActivationTime = 1589544000; /** * The message start string is designed to be unlikely to occur in * normal data. The characters are rarely used upper ASCII, not valid as * UTF-8, and produce a large 32-bit integer with any alignment. */ diskMagic[0] = 0xf9; diskMagic[1] = 0xbe; diskMagic[2] = 0xb4; diskMagic[3] = 0xd9; netMagic[0] = 0xe3; netMagic[1] = 0xe1; netMagic[2] = 0xf3; netMagic[3] = 0xe8; nDefaultPort = 8333; nPruneAfterHeight = 100000; genesis = CreateGenesisBlock(1231006505, 2083236893, 0x1d00ffff, 1, 50 * COIN); consensus.hashGenesisBlock = genesis.GetHash(); assert(consensus.hashGenesisBlock == uint256S("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1" "b60a8ce26f")); assert(genesis.hashMerkleRoot == uint256S("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b" "7afdeda33b")); // Note that of those which support the service bits prefix, most only // support a subset of possible options. This is fine at runtime as // we'll fall back to using them as a oneshot if they don't support the // service bits we want, but we should get them updated to support all // service bits wanted by any release ASAP to avoid it where possible. // Bitcoin ABC seeder vSeeds.emplace_back("seed.bitcoinabc.org"); // bitcoinforks seeders vSeeds.emplace_back("seed-abc.bitcoinforks.org"); // BU backed seeder vSeeds.emplace_back("btccash-seeder.bitcoinunlimited.info"); // Bitprim vSeeds.emplace_back("seed.bitprim.org"); // Amaury SÉCHET vSeeds.emplace_back("seed.deadalnix.me"); // BCHD vSeeds.emplace_back("seed.bchd.cash"); base58Prefixes[PUBKEY_ADDRESS] = std::vector(1, 0); base58Prefixes[SCRIPT_ADDRESS] = std::vector(1, 5); base58Prefixes[SECRET_KEY] = std::vector(1, 128); base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x88, 0xB2, 0x1E}; base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4}; cashaddrPrefix = "bitcoincash"; vFixedSeeds = std::vector( pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main)); fDefaultConsistencyChecks = false; fRequireStandard = true; fMineBlocksOnDemand = false; checkpointData = { .mapCheckpoints = { {11111, uint256S("0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee" "92559f542fdb26e7c1d")}, {33333, uint256S("000000002dd5588a74784eaa7ab0507a18ad16a236e7b" "1ce69f00d7ddfb5d0a6")}, {74000, uint256S("0000000000573993a3c9e41ce34471c079dcf5f52a0e8" "24a81e7f953b8661a20")}, {105000, uint256S("00000000000291ce28027faea320c8d2b054b2e0fe44" "a773f3eefb151d6bdc97")}, {134444, uint256S("00000000000005b12ffd4cd315cd34ffd4a594f430ac" "814c91184a0d42d2b0fe")}, {168000, uint256S("000000000000099e61ea72015e79632f216fe6cb33d7" "899acb35b75c8303b763")}, {193000, uint256S("000000000000059f452a5f7340de6682a977387c1701" "0ff6e6c3bd83ca8b1317")}, {210000, uint256S("000000000000048b95347e83192f69cf0366076336c6" "39f9b7228e9ba171342e")}, {216116, uint256S("00000000000001b4f4b433e81ee46494af945cf96014" "816a4e2370f11b23df4e")}, {225430, uint256S("00000000000001c108384350f74090433e7fcf79a606" "b8e797f065b130575932")}, {250000, uint256S("000000000000003887df1f29024b06fc2200b55f8af8" "f35453d7be294df2d214")}, {279000, uint256S("0000000000000001ae8c72a0b0c301f67e3afca10e81" "9efa9041e458e9bd7e40")}, {295000, uint256S("00000000000000004d9b4ef50f0f9d686fd69db2e03a" "f35a100370c64632a983")}, // UAHF fork block. {478558, uint256S("0000000000000000011865af4122fe3b144e2cbeea86" "142e8ff2fb4107352d43")}, // Nov, 13 DAA activation block. {504031, uint256S("0000000000000000011ebf65b60d0a3de80b8175be70" "9d653b4c1a1beeb6ab9c")}, // Monolith activation. {530359, uint256S("0000000000000000011ada8bd08f46074f44a8f15539" "6f43e38acf9501c49103")}, // Magnetic anomaly activation. {556767, uint256S("0000000000000000004626ff6e3b936941d341c5932e" "ce4357eeccac44e6d56c")}, // Great wall activation. {582680, uint256S("000000000000000001b4b8e36aec7d4f9671a47872cb" "9a74dc16ca398c7dcc18")}, }}; // Data as of block // 000000000000000001d2ce557406b017a928be25ee98906397d339c3f68eec5d // (height 523992). chainTxData = ChainTxData{ // UNIX timestamp of last known number of transactions. 1522608016, // Total number of transactions between genesis and that timestamp // (the tx=... number in the ChainStateFlushed debug.log lines) 248589038, // Estimated number of transactions per second after that timestamp. 3.2}; } }; /** * Testnet (v3) */ class CTestNetParams : public CChainParams { public: CTestNetParams() { strNetworkID = "test"; consensus.nSubsidyHalvingInterval = 210000; // 00000000040b4e986385315e14bee30ad876d8b47f748025b26683116d21aa65 consensus.BIP16Height = 514; consensus.BIP34Height = 21111; consensus.BIP34Hash = uint256S( "0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8"); // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6 consensus.BIP65Height = 581885; // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182 consensus.BIP66Height = 330776; // 00000000025e930139bac5c6c31a403776da130831ab85be56578f3fa75369bb consensus.CSVHeight = 770112; consensus.powLimit = uint256S( "00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // two weeks consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; consensus.nPowTargetSpacing = 10 * 60; consensus.fPowAllowMinDifficultyBlocks = true; consensus.fPowNoRetargeting = false; // The best chain should have at least this much work. consensus.nMinimumChainWork = ChainParamsConstants::TESTNET_MINIMUM_CHAIN_WORK; // By default assume that the signatures in ancestors of this block are // valid. consensus.defaultAssumeValid = ChainParamsConstants::TESTNET_DEFAULT_ASSUME_VALID; // August 1, 2017 hard fork consensus.uahfHeight = 1155875; // November 13, 2017 hard fork consensus.daaHeight = 1188697; // November 15, 2018 hard fork consensus.magneticAnomalyHeight = 1267996; // Nov 15, 2019 12:00:00 UTC protocol upgrade consensus.gravitonActivationTime = 1573819200; // May 15, 2020 12:00:00 UTC protocol upgrade consensus.phononActivationTime = 1589544000; diskMagic[0] = 0x0b; diskMagic[1] = 0x11; diskMagic[2] = 0x09; diskMagic[3] = 0x07; netMagic[0] = 0xf4; netMagic[1] = 0xe5; netMagic[2] = 0xf3; netMagic[3] = 0xf4; nDefaultPort = 18333; nPruneAfterHeight = 1000; genesis = CreateGenesisBlock(1296688602, 414098458, 0x1d00ffff, 1, 50 * COIN); consensus.hashGenesisBlock = genesis.GetHash(); assert(consensus.hashGenesisBlock == uint256S("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526" "f8d77f4943")); assert(genesis.hashMerkleRoot == uint256S("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b" "7afdeda33b")); vFixedSeeds.clear(); vSeeds.clear(); // nodes with support for servicebits filtering should be at the top // Bitcoin ABC seeder vSeeds.emplace_back("testnet-seed.bitcoinabc.org"); // bitcoinforks seeders vSeeds.emplace_back("testnet-seed-abc.bitcoinforks.org"); // Bitprim vSeeds.emplace_back("testnet-seed.bitprim.org"); // Amaury SÉCHET vSeeds.emplace_back("testnet-seed.deadalnix.me"); + // BCHD + vSeeds.emplace_back("testnet-seed.bchd.cash"); base58Prefixes[PUBKEY_ADDRESS] = std::vector(1, 111); base58Prefixes[SCRIPT_ADDRESS] = std::vector(1, 196); base58Prefixes[SECRET_KEY] = std::vector(1, 239); base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF}; base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94}; cashaddrPrefix = "bchtest"; vFixedSeeds = std::vector( pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test)); fDefaultConsistencyChecks = false; fRequireStandard = false; fMineBlocksOnDemand = false; checkpointData = { .mapCheckpoints = { {546, uint256S("000000002a936ca763904c3c35fce2f3556c559c0214345" "d31b1bcebf76acb70")}, // UAHF fork block. {1155875, uint256S("00000000f17c850672894b9a75b63a1e72830bbd5f4" "c8889b5c1a80e7faef138")}, // Nov, 13. DAA activation block. {1188697, uint256S("0000000000170ed0918077bde7b4d36cc4c91be69fa" "09211f748240dabe047fb")}, // Great wall activation. {1303885, uint256S("00000000000000479138892ef0e4fa478ccc938fb94" "df862ef5bde7e8dee23d3")}, }}; // Data as of block // 000000000005b07ecf85563034d13efd81c1a29e47e22b20f4fc6919d5b09cd6 // (height 1223263) chainTxData = ChainTxData{1522608381, 15052068, 0.15}; } }; /** * Regression test */ class CRegTestParams : public CChainParams { public: CRegTestParams() { strNetworkID = "regtest"; consensus.nSubsidyHalvingInterval = 150; // always enforce P2SH BIP16 on regtest consensus.BIP16Height = 0; // BIP34 has not activated on regtest (far in the future so block v1 are // not rejected in tests) consensus.BIP34Height = 100000000; consensus.BIP34Hash = uint256(); // BIP65 activated on regtest (Used in rpc activation tests) consensus.BIP65Height = 1351; // BIP66 activated on regtest (Used in rpc activation tests) consensus.BIP66Height = 1251; // CSV activated on regtest (Used in rpc activation tests) consensus.CSVHeight = 576; consensus.powLimit = uint256S( "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // two weeks consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; consensus.nPowTargetSpacing = 10 * 60; consensus.fPowAllowMinDifficultyBlocks = true; consensus.fPowNoRetargeting = true; // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00"); // By default assume that the signatures in ancestors of this block are // valid. consensus.defaultAssumeValid = uint256S("0x00"); // UAHF is always enabled on regtest. consensus.uahfHeight = 0; // November 13, 2017 hard fork is always on on regtest. consensus.daaHeight = 0; // November 15, 2018 hard fork is always on on regtest. consensus.magneticAnomalyHeight = 0; // Nov 15, 2019 12:00:00 UTC protocol upgrade consensus.gravitonActivationTime = 1573819200; // May 15, 2020 12:00:00 UTC protocol upgrade consensus.phononActivationTime = 1589544000; diskMagic[0] = 0xfa; diskMagic[1] = 0xbf; diskMagic[2] = 0xb5; diskMagic[3] = 0xda; netMagic[0] = 0xda; netMagic[1] = 0xb5; netMagic[2] = 0xbf; netMagic[3] = 0xfa; nDefaultPort = 18444; nPruneAfterHeight = 1000; genesis = CreateGenesisBlock(1296688602, 2, 0x207fffff, 1, 50 * COIN); consensus.hashGenesisBlock = genesis.GetHash(); assert(consensus.hashGenesisBlock == uint256S("0x0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b" "1a11466e2206")); assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab212" "7b7afdeda33b")); //!< Regtest mode doesn't have any fixed seeds. vFixedSeeds.clear(); //!< Regtest mode doesn't have any DNS seeds. vSeeds.clear(); fDefaultConsistencyChecks = true; fRequireStandard = false; fMineBlocksOnDemand = true; checkpointData = {.mapCheckpoints = { {0, uint256S("0f9188f13cb7b2c71f2a335e3a4fc328bf5" "beb436012afca590b1a11466e2206")}, }}; chainTxData = ChainTxData{0, 0, 0}; base58Prefixes[PUBKEY_ADDRESS] = std::vector(1, 111); base58Prefixes[SCRIPT_ADDRESS] = std::vector(1, 196); base58Prefixes[SECRET_KEY] = std::vector(1, 239); base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF}; base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94}; cashaddrPrefix = "bchreg"; } }; static std::unique_ptr globalChainParams; const CChainParams &Params() { assert(globalChainParams); return *globalChainParams; } std::unique_ptr CreateChainParams(const std::string &chain) { if (chain == CBaseChainParams::MAIN) { return std::unique_ptr(new CMainParams()); } if (chain == CBaseChainParams::TESTNET) { return std::unique_ptr(new CTestNetParams()); } if (chain == CBaseChainParams::REGTEST) { return std::unique_ptr(new CRegTestParams()); } throw std::runtime_error( strprintf("%s: Unknown chain %s.", __func__, chain)); } void SelectParams(const std::string &network) { SelectBaseParams(network); globalChainParams = CreateChainParams(network); } diff --git a/src/seeder/main.cpp b/src/seeder/main.cpp index 4c3060ee0..9a45ca24d 100644 --- a/src/seeder/main.cpp +++ b/src/seeder/main.cpp @@ -1,600 +1,601 @@ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const std::function G_TRANSLATION_FUN = nullptr; static const int DEFAULT_NUM_THREADS = 96; static const int DEFAULT_PORT = 53; static const int DEFAULT_NUM_DNS_THREADS = 4; static const bool DEFAULT_TESTNET = false; static const bool DEFAULT_WIPE_BAN = false; static const bool DEFAULT_WIPE_IGNORE = false; static const std::string DEFAULT_EMAIL = ""; static const std::string DEFAULT_NAMESERVER = ""; static const std::string DEFAULT_HOST = ""; static const std::string DEFAULT_TOR_PROXY = ""; static const std::string DEFAULT_IPV4_PROXY = ""; static const std::string DEFAULT_IPV6_PROXY = ""; class CDnsSeedOpts { public: int nThreads; int nPort; int nDnsThreads; int fUseTestNet; int fWipeBan; int fWipeIgnore; std::string mbox; std::string ns; std::string host; std::string tor; std::string ipv4_proxy; std::string ipv6_proxy; std::set filter_whitelist; CDnsSeedOpts() : nThreads(DEFAULT_NUM_THREADS), nPort(DEFAULT_PORT), nDnsThreads(DEFAULT_NUM_DNS_THREADS), fUseTestNet(DEFAULT_TESTNET), fWipeBan(DEFAULT_WIPE_BAN), fWipeIgnore(DEFAULT_WIPE_IGNORE), mbox(DEFAULT_EMAIL), ns(DEFAULT_NAMESERVER), host(DEFAULT_HOST), tor(DEFAULT_TOR_PROXY), ipv4_proxy(DEFAULT_IPV4_PROXY), ipv6_proxy(DEFAULT_IPV6_PROXY) {} void ParseCommandLine(int argc, char **argv) { static const char *help = "Bitcoin-cash-seeder\n" "Usage: %s -h -n [-m ] [-t ] [-p " "]\n" "\n" "Options:\n" "-h Hostname of the DNS seed\n" "-n Hostname of the nameserver\n" "-m E-Mail address reported in SOA records\n" "-t Number of crawlers to run in parallel (default " "96)\n" "-d Number of DNS server threads (default 4)\n" "-p UDP port to listen on (default 53)\n" "-o Tor proxy IP/Port\n" "-i IPV4 SOCKS5 proxy IP/Port\n" "-k IPV6 SOCKS5 proxy IP/Port\n" "-w f1,f2,... Allow these flag combinations as filters\n" "--testnet Use testnet\n" "--wipeban Wipe list of banned nodes\n" "--wipeignore Wipe list of ignored nodes\n" "-?, --help Show this text\n" "\n"; bool showHelp = false; while (1) { static struct option long_options[] = { {"host", required_argument, 0, 'h'}, {"ns", required_argument, 0, 'n'}, {"mbox", required_argument, 0, 'm'}, {"threads", required_argument, 0, 't'}, {"dnsthreads", required_argument, 0, 'd'}, {"port", required_argument, 0, 'p'}, {"onion", required_argument, 0, 'o'}, {"proxyipv4", required_argument, 0, 'i'}, {"proxyipv6", required_argument, 0, 'k'}, {"filter", required_argument, 0, 'w'}, {"testnet", no_argument, &fUseTestNet, 1}, {"wipeban", no_argument, &fWipeBan, 1}, {"wipeignore", no_argument, &fWipeIgnore, 1}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; int option_index = 0; int c = getopt_long(argc, argv, "h:n:m:t:p:d:o:i:k:w:", long_options, &option_index); if (c == -1) break; switch (c) { case 'h': { host = std::string(optarg); break; } case 'm': { mbox = std::string(optarg); break; } case 'n': { ns = std::string(optarg); break; } case 't': { int n = strtol(optarg, nullptr, 10); if (n > 0 && n < 1000) nThreads = n; break; } case 'd': { int n = strtol(optarg, nullptr, 10); if (n > 0 && n < 1000) nDnsThreads = n; break; } case 'p': { int p = strtol(optarg, nullptr, 10); if (p > 0 && p < 65536) nPort = p; break; } case 'o': { tor = std::string(optarg); break; } case 'i': { ipv4_proxy = std::string(optarg); break; } case 'k': { ipv6_proxy = std::string(optarg); break; } case 'w': { char *ptr = optarg; while (*ptr != 0) { unsigned long l = strtoul(ptr, &ptr, 0); if (*ptr == ',') { ptr++; } else if (*ptr != 0) { break; } filter_whitelist.insert(l); } break; } case '?': { showHelp = true; break; } } } if (filter_whitelist.empty()) { filter_whitelist.insert(NODE_NETWORK); filter_whitelist.insert(NODE_NETWORK | NODE_BLOOM); filter_whitelist.insert(NODE_NETWORK | NODE_XTHIN); filter_whitelist.insert(NODE_NETWORK | NODE_BLOOM | NODE_XTHIN); } if (!host.empty() && ns.empty()) showHelp = true; if (showHelp) fprintf(stderr, help, argv[0]); } }; extern "C" { #include } CAddrDb db; extern "C" void *ThreadCrawler(void *data) { int *nThreads = (int *)data; do { std::vector ips; int wait = 5; db.GetMany(ips, 16, wait); int64_t now = time(nullptr); if (ips.empty()) { wait *= 1000; wait += rand() % (500 * *nThreads); Sleep(wait); continue; } std::vector addr; for (size_t i = 0; i < ips.size(); i++) { CServiceResult &res = ips[i]; res.nBanTime = 0; res.nClientV = 0; res.nHeight = 0; res.strClientV = ""; bool getaddr = res.ourLastSuccess + 86400 < now; res.fGood = TestNode(res.service, res.nBanTime, res.nClientV, res.strClientV, res.nHeight, getaddr ? &addr : nullptr); } db.ResultMany(ips); db.Add(addr); } while (1); return nullptr; } extern "C" uint32_t GetIPList(void *thread, char *requestedHostname, addr_t *addr, uint32_t max, uint32_t ipv4, uint32_t ipv6); class CDnsThread { public: struct FlagSpecificData { int nIPv4, nIPv6; std::vector cache; time_t cacheTime; unsigned int cacheHits; FlagSpecificData() : nIPv4(0), nIPv6(0), cacheTime(0), cacheHits(0) {} }; dns_opt_t dns_opt; // must be first const int id; std::map perflag; std::atomic dbQueries; std::set filterWhitelist; void cacheHit(uint64_t requestedFlags, bool force = false) { static bool nets[NET_MAX] = {}; if (!nets[NET_IPV4]) { nets[NET_IPV4] = true; nets[NET_IPV6] = true; } time_t now = time(nullptr); FlagSpecificData &thisflag = perflag[requestedFlags]; thisflag.cacheHits++; if (force || thisflag.cacheHits * 400 > (thisflag.cache.size() * thisflag.cache.size()) || (thisflag.cacheHits * thisflag.cacheHits * 20 > thisflag.cache.size() && (now - thisflag.cacheTime > 5))) { std::set ips; db.GetIPs(ips, requestedFlags, 1000, nets); dbQueries++; thisflag.cache.clear(); thisflag.nIPv4 = 0; thisflag.nIPv6 = 0; thisflag.cache.reserve(ips.size()); for (auto &ip : ips) { struct in_addr addr; struct in6_addr addr6; if (ip.GetInAddr(&addr)) { addr_t a; a.v = 4; memcpy(&a.data.v4, &addr, 4); thisflag.cache.push_back(a); thisflag.nIPv4++; } else if (ip.GetIn6Addr(&addr6)) { addr_t a; a.v = 6; memcpy(&a.data.v6, &addr6, 16); thisflag.cache.push_back(a); thisflag.nIPv6++; } } thisflag.cacheHits = 0; thisflag.cacheTime = now; } } CDnsThread(CDnsSeedOpts *opts, int idIn) : id(idIn) { dns_opt.host = opts->host.c_str(); dns_opt.ns = opts->ns.c_str(); dns_opt.mbox = opts->mbox.c_str(); dns_opt.datattl = 3600; dns_opt.nsttl = 40000; dns_opt.cb = GetIPList; dns_opt.port = opts->nPort; dns_opt.nRequests = 0; dbQueries = 0; perflag.clear(); filterWhitelist = opts->filter_whitelist; } void run() { dnsserver(&dns_opt); } }; extern "C" uint32_t GetIPList(void *data, char *requestedHostname, addr_t *addr, uint32_t max, uint32_t ipv4, uint32_t ipv6) { CDnsThread *thread = (CDnsThread *)data; uint64_t requestedFlags = 0; int hostlen = strlen(requestedHostname); if (hostlen > 1 && requestedHostname[0] == 'x' && requestedHostname[1] != '0') { char *pEnd; uint64_t flags = (uint64_t)strtoull(requestedHostname + 1, &pEnd, 16); if (*pEnd == '.' && pEnd <= requestedHostname + 17 && std::find(thread->filterWhitelist.begin(), thread->filterWhitelist.end(), flags) != thread->filterWhitelist.end()) { requestedFlags = flags; } else { return 0; } } else if (strcasecmp(requestedHostname, thread->dns_opt.host)) { return 0; } thread->cacheHit(requestedFlags); auto &thisflag = thread->perflag[requestedFlags]; uint32_t size = thisflag.cache.size(); uint32_t maxmax = (ipv4 ? thisflag.nIPv4 : 0) + (ipv6 ? thisflag.nIPv6 : 0); if (max > size) { max = size; } if (max > maxmax) { max = maxmax; } uint32_t i = 0; while (i < max) { uint32_t j = i + (rand() % (size - i)); do { bool ok = (ipv4 && thisflag.cache[j].v == 4) || (ipv6 && thisflag.cache[j].v == 6); if (ok) { break; } j++; if (j == size) { j = i; } } while (1); addr[i] = thisflag.cache[j]; thisflag.cache[j] = thisflag.cache[i]; thisflag.cache[i] = addr[i]; i++; } return max; } std::vector dnsThread; extern "C" void *ThreadDNS(void *arg) { CDnsThread *thread = (CDnsThread *)arg; thread->run(); return nullptr; } int StatCompare(const CAddrReport &a, const CAddrReport &b) { if (a.uptime[4] == b.uptime[4]) { if (a.uptime[3] == b.uptime[3]) { return a.clientVersion > b.clientVersion; } else { return a.uptime[3] > b.uptime[3]; } } else { return a.uptime[4] > b.uptime[4]; } } extern "C" void *ThreadDumper(void *) { int count = 0; do { // First 100s, than 200s, 400s, 800s, 1600s, and then 3200s forever Sleep(100000 << count); if (count < 5) { count++; } { std::vector v = db.GetAll(); sort(v.begin(), v.end(), StatCompare); FILE *f = fsbridge::fopen("dnsseed.dat.new", "w+"); if (f) { { CAutoFile cf(f, SER_DISK, CLIENT_VERSION); cf << db; } rename("dnsseed.dat.new", "dnsseed.dat"); } FILE *d = fsbridge::fopen("dnsseed.dump", "w"); fprintf(d, "# address good " "lastSuccess %%(2h) %%(8h) %%(1d) %%(7d) " "%%(30d) blocks svcs version\n"); double stat[5] = {0, 0, 0, 0, 0}; for (CAddrReport rep : v) { fprintf( d, "%-47s %4d %11" PRId64 " %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6i %08" PRIx64 " %5i \"%s\"\n", rep.ip.ToString().c_str(), (int)rep.fGood, rep.lastSuccess, 100.0 * rep.uptime[0], 100.0 * rep.uptime[1], 100.0 * rep.uptime[2], 100.0 * rep.uptime[3], 100.0 * rep.uptime[4], rep.blocks, rep.services, rep.clientVersion, rep.clientSubVersion.c_str()); stat[0] += rep.uptime[0]; stat[1] += rep.uptime[1]; stat[2] += rep.uptime[2]; stat[3] += rep.uptime[3]; stat[4] += rep.uptime[4]; } fclose(d); FILE *ff = fsbridge::fopen("dnsstats.log", "a"); fprintf(ff, "%llu %g %g %g %g %g\n", (unsigned long long)(time(nullptr)), stat[0], stat[1], stat[2], stat[3], stat[4]); fclose(ff); } } while (1); return nullptr; } extern "C" void *ThreadStats(void *) { bool first = true; do { char c[256]; time_t tim = time(nullptr); struct tm *tmp = localtime(&tim); strftime(c, 256, "[%y-%m-%d %H:%M:%S]", tmp); CAddrDbStats stats; db.GetStats(stats); if (first) { first = false; fprintf(stdout, "\n\n\n\x1b[3A"); } else fprintf(stdout, "\x1b[2K\x1b[u"); fprintf(stdout, "\x1b[s"); uint64_t requests = 0; uint64_t queries = 0; for (unsigned int i = 0; i < dnsThread.size(); i++) { requests += dnsThread[i]->dns_opt.nRequests; queries += dnsThread[i]->dbQueries; } fprintf(stdout, "%s %i/%i available (%i tried in %is, %i new, %i active), %i " "banned; %llu DNS requests, %llu db queries\n", c, stats.nGood, stats.nAvail, stats.nTracked, stats.nAge, stats.nNew, stats.nAvail - stats.nTracked - stats.nNew, stats.nBanned, (unsigned long long)requests, (unsigned long long)queries); Sleep(1000); } while (1); return nullptr; } static const std::string mainnet_seeds[] = { "seed.bitcoinabc.org", "seed-abc.bitcoinforks.org", "seed.bitprim.org", "seed.deadalnix.me", "seed.bchd.cash", ""}; static const std::string testnet_seeds[] = { "testnet-seed.bitcoinabc.org", "testnet-seed-abc.bitcoinforks.org", - "testnet-seed.bitprim.org", "testnet-seed.deadalnix.me", ""}; + "testnet-seed.bitprim.org", "testnet-seed.deadalnix.me", + "testnet-seed.bchd.cash", ""}; static const std::string *seeds = mainnet_seeds; const static unsigned int MAX_HOSTS_PER_SEED = 128; extern "C" void *ThreadSeeder(void *) { do { for (int i = 0; seeds[i] != ""; i++) { std::vector ips; LookupHost(seeds[i].c_str(), ips, MAX_HOSTS_PER_SEED, true); for (auto &ip : ips) { db.Add(CAddress(CService(ip, GetDefaultPort()), ServiceFlags()), true); } } Sleep(1800000); } while (1); return nullptr; } int main(int argc, char **argv) { // The logger dump everything on the console by default. GetLogger().m_print_to_console = true; signal(SIGPIPE, SIG_IGN); setbuf(stdout, nullptr); CDnsSeedOpts opts; opts.ParseCommandLine(argc, argv); fprintf(stdout, "Supporting whitelisted filters: "); for (std::set::const_iterator it = opts.filter_whitelist.begin(); it != opts.filter_whitelist.end(); it++) { if (it != opts.filter_whitelist.begin()) { fprintf(stdout, ","); } fprintf(stdout, "0x%lx", (unsigned long)*it); } fprintf(stdout, "\n"); if (!opts.tor.empty()) { CService service(LookupNumeric(opts.tor.c_str(), 9050)); if (service.IsValid()) { fprintf(stdout, "Using Tor proxy at %s\n", service.ToStringIPPort().c_str()); SetProxy(NET_ONION, proxyType(service)); } } if (!opts.ipv4_proxy.empty()) { CService service(LookupNumeric(opts.ipv4_proxy.c_str(), 9050)); if (service.IsValid()) { fprintf(stdout, "Using IPv4 proxy at %s\n", service.ToStringIPPort().c_str()); SetProxy(NET_IPV4, proxyType(service)); } } if (!opts.ipv6_proxy.empty()) { CService service(LookupNumeric(opts.ipv6_proxy.c_str(), 9050)); if (service.IsValid()) { fprintf(stdout, "Using IPv6 proxy at %s\n", service.ToStringIPPort().c_str()); SetProxy(NET_IPV6, proxyType(service)); } } bool fDNS = true; if (opts.fUseTestNet) { fprintf(stdout, "Using testnet.\n"); netMagic[0] = 0xf4; netMagic[1] = 0xe5; netMagic[2] = 0xf3; netMagic[3] = 0xf4; seeds = testnet_seeds; fTestNet = true; } if (opts.ns.empty()) { fprintf(stdout, "No nameserver set. Not starting DNS server.\n"); fDNS = false; } if (fDNS && opts.host.empty()) { fprintf(stderr, "No hostname set. Please use -h.\n"); exit(1); } if (fDNS && opts.mbox.empty()) { fprintf(stderr, "No e-mail address set. Please use -m.\n"); exit(1); } FILE *f = fsbridge::fopen("dnsseed.dat", "r"); if (f) { fprintf(stdout, "Loading dnsseed.dat..."); CAutoFile cf(f, SER_DISK, CLIENT_VERSION); cf >> db; if (opts.fWipeBan) { db.banned.clear(); fprintf(stdout, "Ban list wiped..."); } if (opts.fWipeIgnore) { db.ResetIgnores(); fprintf(stdout, "Ignore list wiped..."); } fprintf(stdout, "done\n"); } pthread_t threadDns, threadSeed, threadDump, threadStats; if (fDNS) { fprintf(stdout, "Starting %i DNS threads for %s on %s (port %i)...", opts.nDnsThreads, opts.host.c_str(), opts.ns.c_str(), opts.nPort); dnsThread.clear(); for (int i = 0; i < opts.nDnsThreads; i++) { dnsThread.push_back(new CDnsThread(&opts, i)); pthread_create(&threadDns, nullptr, ThreadDNS, dnsThread[i]); fprintf(stdout, "."); Sleep(20); } fprintf(stdout, "done\n"); } fprintf(stdout, "Starting seeder..."); pthread_create(&threadSeed, nullptr, ThreadSeeder, nullptr); fprintf(stdout, "done\n"); fprintf(stdout, "Starting %i crawler threads...", opts.nThreads); pthread_attr_t attr_crawler; pthread_attr_init(&attr_crawler); pthread_attr_setstacksize(&attr_crawler, 0x20000); for (int i = 0; i < opts.nThreads; i++) { pthread_t thread; pthread_create(&thread, &attr_crawler, ThreadCrawler, &opts.nThreads); } pthread_attr_destroy(&attr_crawler); fprintf(stdout, "done\n"); pthread_create(&threadStats, nullptr, ThreadStats, nullptr); pthread_create(&threadDump, nullptr, ThreadDumper, nullptr); void *res; pthread_join(threadDump, &res); return 0; }