diff --git a/.gitignore b/.gitignore index 1322d7b68f..a6cce7f8d9 100644 --- a/.gitignore +++ b/.gitignore @@ -144,3 +144,4 @@ nbproject/ #Local Development Environment - Visual Studio Code .vscode +.ccache \ No newline at end of file diff --git a/cmake/modules/NSIS.template.in b/cmake/modules/NSIS.template.in index 6526eb5687..e919daa00e 100644 --- a/cmake/modules/NSIS.template.in +++ b/cmake/modules/NSIS.template.in @@ -99,6 +99,7 @@ Section -post SEC0001 CreateShortcut "$SMPROGRAMS\$StartMenuGroup\@CPACK_NSIS_DISPLAY_NAME@ (testnet4).lnk" "$INSTDIR\${BITCOIN_GUI_NAME}" "-testnet4" "$INSTDIR\${BITCOIN_GUI_NAME}" 1 CreateShortcut "$SMPROGRAMS\$StartMenuGroup\@CPACK_NSIS_DISPLAY_NAME@ (scalenet).lnk" "$INSTDIR\${BITCOIN_GUI_NAME}" "-scalenet" "$INSTDIR\${BITCOIN_GUI_NAME}" 1 CreateShortcut "$SMPROGRAMS\$StartMenuGroup\@CPACK_NSIS_DISPLAY_NAME@ (chipnet).lnk" "$INSTDIR\${BITCOIN_GUI_NAME}" "-chipnet" "$INSTDIR\${BITCOIN_GUI_NAME}" 1 + CreateShortcut "$SMPROGRAMS\$StartMenuGroup\@CPACK_NSIS_DISPLAY_NAME@ (tempnet).lnk" "$INSTDIR\${BITCOIN_GUI_NAME}" "-tempnet" "$INSTDIR\${BITCOIN_GUI_NAME}" 1 CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Uninstall $(^Name).lnk" $INSTDIR\uninstall.exe !insertmacro MUI_STARTMENU_WRITE_END WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayName "$(^Name)" @@ -146,11 +147,13 @@ Section -un.post UNSEC0001 Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\@CPACK_NSIS_DISPLAY_NAME@ (testnet4).lnk" Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\@CPACK_NSIS_DISPLAY_NAME@ (scalenet).lnk" Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\@CPACK_NSIS_DISPLAY_NAME@ (chipnet).lnk" + Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\@CPACK_NSIS_DISPLAY_NAME@ (tempnet).lnk" Delete /REBOOTOK "$SMSTARTUP\Bitcoin Cash Node.lnk" Delete /REBOOTOK "$SMSTARTUP\Bitcoin Cash Node (testnet).lnk" Delete /REBOOTOK "$SMSTARTUP\Bitcoin Cash Node (testnet4).lnk" Delete /REBOOTOK "$SMSTARTUP\Bitcoin Cash Node (scalenet).lnk" Delete /REBOOTOK "$SMSTARTUP\Bitcoin Cash Node (chipnet).lnk" + Delete /REBOOTOK "$SMSTARTUP\Bitcoin Cash Node (tempnet).lnk" Delete /REBOOTOK "$SMSTARTUP\Bitcoin Cash Node (regtest).lnk" Delete /REBOOTOK "$INSTDIR\uninstall.exe" Delete /REBOOTOK "$INSTDIR\debug.log" diff --git a/contrib/devtools/chainparams/make_chainparams.py b/contrib/devtools/chainparams/make_chainparams.py index 42ef6347ff..f0d76362eb 100755 --- a/contrib/devtools/chainparams/make_chainparams.py +++ b/contrib/devtools/chainparams/make_chainparams.py @@ -19,6 +19,7 @@ class Chain(Enum): TestNet4 = "TESTNET4" ScaleNet = "SCALENET" ChipNet = "CHIPNET" + TempNet = "TEMPNET" def get_chainparams(rpc_caller, block): @@ -38,6 +39,8 @@ def get_chainparams(rpc_caller, block): "edit this script to comment-out this line of code.") elif chaininfo['chain'] == 'chip': chain = Chain.ChipNet + elif chaininfo['chain'] == 'temp': + chain = Chain.TempNet else: raise NotImplementedError @@ -86,7 +89,7 @@ def main(args): formatter_class=argparse.RawTextHelpFormatter) parser.add_argument('--address', '-a', default="127.0.0.1:8332", help="Node address for making RPC calls.\n" - "The chain (MainNet, TestNet, TestNet4, ScaleNet) will be automatically detected.\n" + "The chain (MainNet, TestNet, TestNet4, ScaleNet, ChipNet, TempNet) will be automatically detected.\n" "Default: '127.0.0.1:8332'") parser.add_argument('--block', '-b', help="The block hash or height to use for fetching chainparams.\n" diff --git a/contrib/devtools/gen-bitcoin-conf.sh b/contrib/devtools/gen-bitcoin-conf.sh index 68e3b299ba..22cd9df9ca 100755 --- a/contrib/devtools/gen-bitcoin-conf.sh +++ b/contrib/devtools/gen-bitcoin-conf.sh @@ -64,7 +64,7 @@ ${BITCOIND} --help \ cat >> "${EXAMPLE_CONF_FILE}" << 'EOF' # [Sections] -# Most options apply to mainnet, testnet, testnet4, scalenet, chipnet, and regtest. +# Most options apply to mainnet, testnet, testnet4, scalenet, chipnet, tempnet, and regtest. # If you want to confine an option to just one network, you should add it in the # relevant section below. # EXCEPTIONS: The options addnode, connect, port, bind, rpcport, rpcbind and wallet @@ -85,6 +85,9 @@ cat >> "${EXAMPLE_CONF_FILE}" << 'EOF' # Options only for chipnet [chip] +# Options only for tempnet +[temp] + # Options only for regtest [regtest] EOF diff --git a/doc/test-networks.md b/doc/test-networks.md index 25db9a2895..11cd1e814c 100644 --- a/doc/test-networks.md +++ b/doc/test-networks.md @@ -4,17 +4,18 @@ Test Networks This document describes the Bitcoin Cash test networks supported by the BCHN software. -There are currently three test networks that you can access with BCHN: +There are currently five test networks that you can access with BCHN: - testnet3 (historical testnet) - testnet4 - scalenet - chipnet +- tempnet These test network are maintained and supported by the wider community of protocol developers. They can be accessed by running the software -(daemon, GUI and CLI) with `-testnet`, `-testnet4`, `-scalenet` and -`-chipnet` arguments, respectively. +(daemon, GUI and CLI) with `-testnet`, `-testnet4`, `-scalenet`, +`-chipnet`, and `-tempnet` arguments, respectively. Other software clients may have additional test network definitions compiled into them but these are not currently supported by BCHN and could not be @@ -89,16 +90,29 @@ Chipnet is intended as a place to test against upcoming Cash Improvement Proposals (CHIPs) which are intended to be activated in the next main network consensus upgrade. -It therefore deploys these CHIPs (and updates to them) as much in advance as -possible (optimally 6 months ahead of the main network upgrades). +Chipnet deploys CHIPs 6 months ahead of the main network upgrades. -After the upgrade to Adaptive Blocksize Limit Algorithm for Bitcoin Cash -on this network in November 2023, the maximum blocksize of this network will -vary dynamically with a floor capacity of 2MB. +The maximum blocksize of Chipnet varies dynamically with a floor of 2MB. + +Tempnet +------- + +A tempnet is any temporary fork of Chipnet that tests and previews a potential +Chipnet upgrade. In the months before CHIPs are locked-in for a particular +cycle, possible CHIPs for that cycle should be integrated and tested together +via one or more coordinated public tempnets. + +Tempnet and Chipnet nodes pre-queue a predefined activation transaction for +broadcast at the activation MTP. You can inspect and manage the broadcast +queue via RPC: Overview Table for BCHN-supported Test Networks ----------------------------------------------- +This table summarizes parameters for testnet3, testnet4, scalenet, and chipnet. +Tempnet parameters match chipnet (with the exception of the latest upgrade +activation MTP/height), so it is not listed separately. + | Attribute/Network | testnet3 | testnet4 | scalenet | chipnet | |------------------------------|-------------|--------------|-------------|-------------| | Default p2p port | 18333 | 28333 | 38333 | 48333 | diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7af374d3c7..804114867a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -581,6 +581,7 @@ add_library(server net_processing.cpp node/blockstorage.cpp node/transaction.cpp + node/txbroadcastqueue.cpp noui.cpp outputtype.cpp policy/fees.cpp diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 536c7e64e0..9b8cd25e66 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -41,6 +41,7 @@ static void SetupCliArgs() { const auto testnet4BaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET4); const auto scalenetBaseParams = CreateBaseChainParams(CBaseChainParams::SCALENET); const auto chipnetBaseParams = CreateBaseChainParams(CBaseChainParams::CHIPNET); + const auto tempnetBaseParams = CreateBaseChainParams(CBaseChainParams::TEMPNET); const auto regtestBaseParams = CreateBaseChainParams(CBaseChainParams::REGTEST); gArgs.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, @@ -77,12 +78,13 @@ static void SetupCliArgs() { ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-rpcport=", strprintf("Connect to JSON-RPC on (default: %u, " - "testnet: %u, testnet4: %u, scalenet: %u, chipnet: %u, regtest: %u)", + "testnet: %u, testnet4: %u, scalenet: %u, chipnet: %u, tempnet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), testnet4BaseParams->RPCPort(), scalenetBaseParams->RPCPort(), chipnetBaseParams->RPCPort(), + tempnetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS); gArgs.AddArg("-rpcwait", "Wait for RPC server to start", ArgsManager::ALLOW_ANY, diff --git a/src/chainparams.cpp b/src/chainparams.cpp index d770fc340f..9e332cb0f6 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -9,8 +9,11 @@ #include #include #include +#include #include #include +#include +#include #include #include #include @@ -149,8 +152,8 @@ class CMainParams : public CChainParams { // May 15, 2025 12:00:00 UTC protocol upgrade (this is one less than the first block mined under new rules) consensus.upgrade11Height = 898373; - // May 15, 2026 12:00:00 UTC tentative protocol upgrade - consensus.upgrade12ActivationTime = 1778846400; + consensus.upgrade12ActivationTime = Consensus::UpgradeTimes::MAY_2026; + consensus.softwareExpiryTime = Consensus::UpgradeTimes::MAY_2027; // Default limit for block size (in bytes) consensus.nDefaultConsensusBlockSize = DEFAULT_CONSENSUS_BLOCK_SIZE; @@ -403,8 +406,8 @@ class CTestNetParams : public CChainParams { // May 15, 2025 12:00:00 UTC protocol upgrade (this is one less than the first block mined under new rules) consensus.upgrade11Height = 1658049; - // May 15, 2026 12:00:00 UTC tentative protocol upgrade - consensus.upgrade12ActivationTime = 1778846400; + consensus.upgrade12ActivationTime = Consensus::UpgradeTimes::MAY_2026; + consensus.softwareExpiryTime = Consensus::UpgradeTimes::MAY_2027; // Default limit for block size (in bytes) consensus.nDefaultConsensusBlockSize = DEFAULT_CONSENSUS_BLOCK_SIZE; @@ -620,8 +623,8 @@ class CTestNet4Params : public CChainParams { // May 15, 2025 12:00:00 UTC protocol upgrade (this is one less than the first block mined under new rules) consensus.upgrade11Height = 253318; - // May 15, 2026 12:00:00 UTC tentative protocol upgrade - consensus.upgrade12ActivationTime = 1778846400; + consensus.upgrade12ActivationTime = Consensus::UpgradeTimes::MAY_2026; + consensus.softwareExpiryTime = Consensus::UpgradeTimes::MAY_2027; // Default limit for block size (in bytes) (testnet4 is smaller at 2MB) consensus.nDefaultConsensusBlockSize = 2 * ONE_MEGABYTE; @@ -807,8 +810,8 @@ class CScaleNetParams : public CChainParams { // May 15, 2025 12:00:00 UTC protocol upgrade consensus.upgrade11Height = 10'006; - // May 15, 2026 12:00:00 UTC tentative protocol upgrade - consensus.upgrade12ActivationTime = 1778846400; + consensus.upgrade12ActivationTime = Consensus::UpgradeTimes::MAY_2026; + consensus.softwareExpiryTime = Consensus::UpgradeTimes::MAY_2027; // Default limit for block size (in bytes) consensus.nDefaultConsensusBlockSize = 256 * ONE_MEGABYTE; @@ -967,8 +970,19 @@ class CChipNetParams : public CChainParams { // November 15, 2024 12:00:00 UTC; protocol upgrade activates 6 months early consensus.upgrade11Height = 227228; // (one less than upgrade block) - // November 15, 2025 12:00:00 UTC; tentative protocol upgrade activates 6 months early - consensus.upgrade12ActivationTime = 1763208000; + consensus.upgrade12ActivationTime = Consensus::UpgradeTimes::NOV_2025; + consensus.softwareExpiryTime = Consensus::UpgradeTimes::NOV_2026; + + // Require and pre-queue a predefined activation transaction on chipnet. + static const char *const CHIPNET_ACTIVATION_TX_HEX = + "020000000130d1c3a524c4b208e6c1b558b110749f4ae1d431373b10a963f3ff9d873db75700000000040279015d00000000010000000000000000a9ef30d1c3a524c4b208e6c1b558b110749f4ae1d431373b10a963f3ff9d873db757608000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006a043230323600000000"; + consensus.upgrade12ActivationTx = ParseHex(CHIPNET_ACTIVATION_TX_HEX); + { + CDataStream ss(consensus.upgrade12ActivationTx, SER_NETWORK, PROTOCOL_VERSION); + CMutableTransaction mtx; + ss >> mtx; + consensus.upgrade12ActivationTxid = CTransaction(mtx).GetId(); + } // Default limit for block size (in bytes) (chipnet is like testnet4 in that it is smaller at 2MB) consensus.nDefaultConsensusBlockSize = 2 * ONE_MEGABYTE; @@ -1071,6 +1085,22 @@ class CChipNetParams : public CChainParams { } }; +class CTempNetParams : public CChipNetParams { +public: + CTempNetParams() { + strNetworkID = CBaseChainParams::TEMPNET; + // Tempnets activate the next consensus changes ahead of chipnet. + consensus.upgrade12ActivationTime = 1757937600; // Sep 15 2025 12:00:00 UTC + // Tempnets expire when the real chipnet activates the upgrade: + consensus.softwareExpiryTime = Consensus::UpgradeTimes::NOV_2025; + vSeeds.clear(); + vFixedSeeds.clear(); + vSeeds.emplace_back("tempnet.bitjson.com"); + vSeeds.emplace_back("chipnet.bitjson.com"); + vFixedSeeds.assign(std::begin(pnSeed6_tempnet), std::end(pnSeed6_tempnet)); + } +}; + /** * Regression test */ @@ -1242,6 +1272,10 @@ std::unique_ptr CreateChainParams(const std::string &chain) { return std::make_unique(); } + if (chain == CBaseChainParams::TEMPNET) { + return std::make_unique(); + } + throw std::runtime_error( strprintf("%s: Unknown chain %s.", __func__, chain)); } diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index b6175221e6..cdbc4a8e6f 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -17,6 +17,7 @@ const std::string CBaseChainParams::TESTNET = "test"; const std::string CBaseChainParams::TESTNET4 = "test4"; const std::string CBaseChainParams::SCALENET = "scale"; const std::string CBaseChainParams::CHIPNET = "chip"; +const std::string CBaseChainParams::TEMPNET = "temp"; const std::string CBaseChainParams::REGTEST = "regtest"; void SetupChainParamsBaseOptions() { @@ -33,6 +34,8 @@ void SetupChainParamsBaseOptions() { OptionsCategory::CHAINPARAMS); gArgs.AddArg("-chipnet", "Use the upcoming upgrade activation chain", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); + gArgs.AddArg("-tempnet", "Use the temporary test chain for upcoming chipnet rules", ArgsManager::ALLOW_ANY, + OptionsCategory::CHAINPARAMS); } static std::unique_ptr globalChainBaseParams; @@ -67,6 +70,10 @@ std::unique_ptr CreateBaseChainParams(const std::string &chain return std::make_unique("chipnet", 48332, 48334); } + if (chain == CBaseChainParams::TEMPNET) { + return std::make_unique("tempnet", 48332, 48334); + } + if (chain == CBaseChainParams::REGTEST) { return std::make_unique("regtest", 18443, 18445); } diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h index c65d0dbe09..f0465d1d35 100644 --- a/src/chainparamsbase.h +++ b/src/chainparamsbase.h @@ -23,6 +23,7 @@ class CBaseChainParams { static const std::string TESTNET4; static const std::string SCALENET; static const std::string CHIPNET; + static const std::string TEMPNET; static const std::string REGTEST; const std::string &DataDir() const { return strDataDir; } diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h index 39cf22e8c4..5bcbcb2a89 100644 --- a/src/chainparamsseeds.h +++ b/src/chainparamsseeds.h @@ -205,3 +205,14 @@ static const SeedSpec6 pnSeed6_chipnet[] = { { "72.66.71.69:48333" }, { "208.88.16.38:48333" } }; + +static const SeedSpec6 pnSeed6_tempnet[] = { + { "192.241.252.19:48333" }, + { "165.232.108.105:48333" }, + { "188.166.38.228:48333" }, + { "142.93.105.115:48333" }, + { "146.190.48.81:48333" }, + { "159.89.170.220:48333" }, + { "165.22.53.37:48333" }, + { "134.199.159.4:48333" } +}; diff --git a/src/consensus/activation.cpp b/src/consensus/activation.cpp index 96d09b104f..13eb0f6c77 100644 --- a/src/consensus/activation.cpp +++ b/src/consensus/activation.cpp @@ -107,7 +107,7 @@ int32_t GetUpgrade9ActivationHeight(const Consensus::Params ¶ms) { } bool IsUpgrade9EnabledForHeightPrev(const Consensus::Params ¶ms, const int32_t nHeightPrev) { - return nHeightPrev >= GetUpgrade9ActivationHeight(params); + return nHeightPrev >= g_Upgrade9HeightOverride.value_or(params.upgrade9Height); } bool IsUpgrade9Enabled(const Consensus::Params ¶ms, const CBlockIndex *pindexPrev) { @@ -125,16 +125,13 @@ int32_t GetUpgrade10ActivationHeight(const Consensus::Params ¶ms) { return g_Upgrade10HeightOverride.value_or(params.upgrade10Height); } -static bool IsUpgrade10EnabledForHeightPrev(const Consensus::Params ¶ms, const int32_t nHeightPrev) { - return nHeightPrev >= GetUpgrade10ActivationHeight(params); -} bool IsUpgrade10Enabled(const Consensus::Params ¶ms, const CBlockIndex *pindexPrev) { if (pindexPrev == nullptr) { return false; } - return IsUpgrade10EnabledForHeightPrev(params, pindexPrev->nHeight); + return pindexPrev->nHeight >= g_Upgrade10HeightOverride.value_or(params.upgrade10Height); } // Upgrade 11 @@ -145,7 +142,7 @@ int32_t GetUpgrade11ActivationHeight(const Consensus::Params ¶ms) { } static bool IsUpgrade11EnabledForHeightPrev(const Consensus::Params ¶ms, const int32_t nHeightPrev) { - return nHeightPrev >= GetUpgrade11ActivationHeight(params); + return nHeightPrev >= g_Upgrade11HeightOverride.value_or(params.upgrade11Height); } bool IsUpgrade11Enabled(const Consensus::Params ¶ms, const CBlockIndex *pindexPrev) { @@ -157,14 +154,12 @@ bool IsUpgrade11Enabled(const Consensus::Params ¶ms, const CBlockIndex *pind } // Upgrade 12 -static bool IsUpgrade12Enabled(const Consensus::Params ¶ms, const int64_t nMedianTimePast) { - return nMedianTimePast >= gArgs.GetArg("-upgrade12activationtime", params.upgrade12ActivationTime); -} bool IsUpgrade12Enabled(const Consensus::Params ¶ms, const CBlockIndex *pindexPrev) { if (pindexPrev == nullptr) { return false; } - return IsUpgrade12Enabled(params, pindexPrev->GetMedianTimePast()); + return pindexPrev->GetMedianTimePast() >= + gArgs.GetArg("-upgrade12activationtime", params.upgrade12ActivationTime); } diff --git a/src/consensus/params.h b/src/consensus/params.h index f9d387995b..12e5b4e069 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -12,6 +12,7 @@ #include #include +#include namespace Consensus { @@ -57,6 +58,13 @@ struct Params { int upgrade11Height; /** Unix time used for tentative MTP activation of 15 May 2026 12:00:00 UTC upgrade */ int64_t upgrade12ActivationTime; + /** Default time for software_outdated warnings (non-consensus). <=0 disables. */ + int64_t softwareExpiryTime = 0; + + /** Raw tx required in the first block after tempnet activates (tempnet-only). */ + std::vector upgrade12ActivationTx; + /** Precomputed txid of the activation tx. */ + uint256 upgrade12ActivationTxid; /** Default blocksize limit -- can be overridden with the -excessiveblocksize= command-line switch. After activation of upgrade 10, this is the minimum max block size, since the ABLA algorithm allows for diff --git a/src/consensus/upgrade_times.h b/src/consensus/upgrade_times.h new file mode 100644 index 0000000000..e0a83ff588 --- /dev/null +++ b/src/consensus/upgrade_times.h @@ -0,0 +1,25 @@ +// Copyright (c) 2025 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include + +namespace Consensus { +namespace UpgradeTimes { + +// Median-time-past (UTC 12:00:00) for tentative upgrades. +static constexpr int64_t MAY_2025 = 1747310400; // May 15, 2025 12:00:00 UTC +static constexpr int64_t NOV_2025 = 1763208000; // Nov 15, 2025 12:00:00 UTC +static constexpr int64_t MAY_2026 = 1778846400; // May 15, 2026 12:00:00 UTC +static constexpr int64_t NOV_2026 = 1794744000; // Nov 15, 2026 12:00:00 UTC +static constexpr int64_t MAY_2027 = 1810382400; // May 15, 2027 12:00:00 UTC +static constexpr int64_t NOV_2027 = 1826280000; // Nov 15, 2027 12:00:00 UTC +static constexpr int64_t MAY_2028 = 1842004800; // May 15, 2028 12:00:00 UTC +static constexpr int64_t NOV_2028 = 1857902400; // Nov 15, 2028 12:00:00 UTC +static constexpr int64_t MAY_2029 = 1873540800; // May 15, 2029 12:00:00 UTC +static constexpr int64_t NOV_2029 = 1889438400; // Nov 15, 2029 12:00:00 UTC + +} // namespace UpgradeTimes +} // namespace Consensus diff --git a/src/core_write.cpp b/src/core_write.cpp index 5481a1dbab..1c44ac54f1 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -236,7 +236,8 @@ UniValue::Object ScriptPubKeyToUniv(const Config &config, const CScript &scriptP txnouttype type; std::vector addresses; int nRequired; - const uint32_t flags = STANDARD_SCRIPT_VERIFY_FLAGS | SCRIPT_ENABLE_P2SH_32 | SCRIPT_ENABLE_TOKENS; + const uint32_t flags = STANDARD_SCRIPT_VERIFY_FLAGS | SCRIPT_ENABLE_P2SH_32 | SCRIPT_ENABLE_TOKENS + | SCRIPT_ENABLE_MAY2026; bool extracted = ExtractDestinations(scriptPubKey, type, addresses, nRequired, flags); if (extracted) { diff --git a/src/init.cpp b/src/init.cpp index a1c0814187..038250ebcb 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -38,9 +39,13 @@ #include #include #include +#include +#include +#include #include #include #include +#include #include #include #include @@ -393,12 +398,14 @@ void SetupServerArgs() { const auto regtestBaseParams = CreateBaseChainParams(CBaseChainParams::REGTEST); const auto scalenetBaseParams = CreateBaseChainParams(CBaseChainParams::SCALENET); const auto chipnetBaseParams = CreateBaseChainParams(CBaseChainParams::CHIPNET); + const auto tempnetBaseParams = CreateBaseChainParams(CBaseChainParams::TEMPNET); const auto defaultChainParams = CreateChainParams(CBaseChainParams::MAIN); const auto testnetChainParams = CreateChainParams(CBaseChainParams::TESTNET); const auto testnet4ChainParams = CreateChainParams(CBaseChainParams::TESTNET4); const auto regtestChainParams = CreateChainParams(CBaseChainParams::REGTEST); const auto scalenetChainParams = CreateChainParams(CBaseChainParams::SCALENET); const auto chipnetChainParams = CreateChainParams(CBaseChainParams::CHIPNET); + const auto tempnetChainParams = CreateChainParams(CBaseChainParams::TEMPNET); // Hidden Options std::vector hidden_args = { @@ -486,12 +493,13 @@ void SetupServerArgs() { strprintf("Before upgrade 10 activates: Do not accept blocks larger than this limit, in bytes." " After upgrade 10 activates: The minimum (floor) maximum block size used by the adaptive" " blocksize limit algorithm, in bytes. (default: %u, testnet: %u, testnet4: %u," - " scalenet: %u, chipnet: %u, regtest: %u)", + " scalenet: %u, chipnet: %u, tempnet: %u, regtest: %u)", defaultChainParams->GetConsensus().nDefaultConsensusBlockSize, testnetChainParams->GetConsensus().nDefaultConsensusBlockSize, testnet4ChainParams->GetConsensus().nDefaultConsensusBlockSize, scalenetChainParams->GetConsensus().nDefaultConsensusBlockSize, chipnetChainParams->GetConsensus().nDefaultConsensusBlockSize, + tempnetChainParams->GetConsensus().nDefaultConsensusBlockSize, regtestChainParams->GetConsensus().nDefaultConsensusBlockSize), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-feefilter", @@ -529,12 +537,13 @@ void SetupServerArgs() { "Imports blocks from external blk000??.dat file on startup", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-maxmempool=", strprintf("Keep the transaction memory pool below " - "megabytes (default: %u, testnet: %u, testnet4: %u, scalenet: %u, chipnet: %u)", + "megabytes (default: %u, testnet: %u, testnet4: %u, scalenet: %u, chipnet: %u, tempnet: %u)", DEFAULT_MAX_MEMPOOL_SIZE_PER_MB * defaultChainParams->GetConsensus().nDefaultConsensusBlockSize / ONE_MEGABYTE, DEFAULT_MAX_MEMPOOL_SIZE_PER_MB * testnetChainParams->GetConsensus().nDefaultConsensusBlockSize / ONE_MEGABYTE, DEFAULT_MAX_MEMPOOL_SIZE_PER_MB * testnet4ChainParams->GetConsensus().nDefaultConsensusBlockSize / ONE_MEGABYTE, DEFAULT_MAX_MEMPOOL_SIZE_PER_MB * scalenetChainParams->GetConsensus().nDefaultConsensusBlockSize / ONE_MEGABYTE, - DEFAULT_MAX_MEMPOOL_SIZE_PER_MB * chipnetChainParams->GetConsensus().nDefaultConsensusBlockSize / ONE_MEGABYTE), + DEFAULT_MAX_MEMPOOL_SIZE_PER_MB * chipnetChainParams->GetConsensus().nDefaultConsensusBlockSize / ONE_MEGABYTE, + DEFAULT_MAX_MEMPOOL_SIZE_PER_MB * tempnetChainParams->GetConsensus().nDefaultConsensusBlockSize / ONE_MEGABYTE), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-maxorphantx=", strprintf("Keep at most unconnectable transactions in " @@ -664,10 +673,11 @@ void SetupServerArgs() { "for IPv6. Append =onion to tag any incoming connections to that address and port as " "incoming Tor connections (default: 127.0.0.1:%u=onion, testnet: 127.0.0.1:%u=onion, " "testnet4: 127.0.0.1:%u=onion, scalenet: 127.0.0.1:%u=onion, chipnet: 127.0.0.1:%u=onion, " - "regtest: 127.0.0.1:%u=onion)", + "tempnet: 127.0.0.1:%u=onion, regtest: 127.0.0.1:%u=onion)", defaultBaseParams->OnionServiceTargetPort(), testnetBaseParams->OnionServiceTargetPort(), testnet4BaseParams->OnionServiceTargetPort(), scalenetBaseParams->OnionServiceTargetPort(), - chipnetBaseParams->OnionServiceTargetPort(), regtestBaseParams->OnionServiceTargetPort()), + chipnetBaseParams->OnionServiceTargetPort(), tempnetBaseParams->OnionServiceTargetPort(), + regtestBaseParams->OnionServiceTargetPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); gArgs.AddArg( "-connect=", @@ -743,12 +753,13 @@ void SetupServerArgs() { ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); gArgs.AddArg("-port=", strprintf("Listen for connections on (default: %u, " - "testnet: %u, testnet4: %u, scalenet: %u, chipnet: %u, regtest: %u)", + "testnet: %u, testnet4: %u, scalenet: %u, chipnet: %u, tempnet: %u, regtest: %u)", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), testnet4ChainParams->GetDefaultPort(), scalenetChainParams->GetDefaultPort(), chipnetChainParams->GetDefaultPort(), + tempnetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); gArgs.AddArg("-proxy=", "Connect through SOCKS5 proxy", ArgsManager::ALLOW_ANY, @@ -985,42 +996,52 @@ void SetupServerArgs() { gArgs.AddArg( "-upgrade9activationheight=", strprintf("Activation height of the May 2023 Bitcoin Cash Network Upgrade; first block using new rules will be" - " after this height (default: %d, testnet: %d, testnet4: %d, scalenet: %d, chipnet: %d, regtest: %d)", + " after this height (default: %d, testnet: %d, testnet4: %d, scalenet: %d, chipnet: %d, tempnet: %d, regtest: %d)", defaultChainParams->GetConsensus().upgrade9Height, testnetChainParams->GetConsensus().upgrade9Height, testnet4ChainParams->GetConsensus().upgrade9Height, scalenetChainParams->GetConsensus().upgrade9Height, chipnetChainParams->GetConsensus().upgrade9Height, + tempnetChainParams->GetConsensus().upgrade9Height, regtestChainParams->GetConsensus().upgrade9Height), true, OptionsCategory::DEBUG_TEST); gArgs.AddArg( "-upgrade10activationheight=", strprintf("Activation height of the May 2024 Bitcoin Cash Network Upgrade; first block using new rules will be" - " after this height (default: %d, testnet: %d, testnet4: %d, scalenet: %d, chipnet: %d, regtest: %d)", + " after this height (default: %d, testnet: %d, testnet4: %d, scalenet: %d, chipnet: %d, tempnet: %d, regtest: %d)", defaultChainParams->GetConsensus().upgrade10Height, testnetChainParams->GetConsensus().upgrade10Height, testnet4ChainParams->GetConsensus().upgrade10Height, scalenetChainParams->GetConsensus().upgrade10Height, chipnetChainParams->GetConsensus().upgrade10Height, + tempnetChainParams->GetConsensus().upgrade10Height, regtestChainParams->GetConsensus().upgrade10Height), true, OptionsCategory::DEBUG_TEST); gArgs.AddArg( "-upgrade11activationheight=", strprintf("Activation height of the May 2025 Bitcoin Cash Network Upgrade; first block using new rules will be" - " after this height (default: %d, testnet: %d, testnet4: %d, scalenet: %d, chipnet: %d, regtest: %d)", + " after this height (default: %d, testnet: %d, testnet4: %d, scalenet: %d, chipnet: %d, tempnet: %d, regtest: %d)", defaultChainParams->GetConsensus().upgrade11Height, testnetChainParams->GetConsensus().upgrade11Height, testnet4ChainParams->GetConsensus().upgrade11Height, scalenetChainParams->GetConsensus().upgrade11Height, chipnetChainParams->GetConsensus().upgrade11Height, + tempnetChainParams->GetConsensus().upgrade11Height, regtestChainParams->GetConsensus().upgrade11Height), true, OptionsCategory::DEBUG_TEST); gArgs.AddArg( "-upgrade12activationtime=", strprintf("Activation time of the tentative May 2026 Bitcoin Cash Network Upgrade ( seconds since epoch, " - "default: %d, chipnet: %d)", + "default: %d, chipnet: %d, tempnet: %d)", defaultChainParams->GetConsensus().upgrade12ActivationTime, - chipnetChainParams->GetConsensus().upgrade12ActivationTime), + chipnetChainParams->GetConsensus().upgrade12ActivationTime, + tempnetChainParams->GetConsensus().upgrade12ActivationTime), + true, OptionsCategory::DEBUG_TEST); + + gArgs.AddArg( + "-upgrade12activationtx=", + "Hex-encoded raw transaction to require in the first block after the tentative May 2026 upgrade activates " + "(chipnet/tempnet; optional override of the preset). On other networks this option is ignored.", true, OptionsCategory::DEBUG_TEST); gArgs.AddArg( "-printtoconsole", @@ -1123,25 +1144,27 @@ void SetupServerArgs() { ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY); gArgs.AddArg("-blockmaxsize=", - strprintf("Set maximum mined block size in bytes (default: %u, testnet: %u, testnet4: %u," - " scalenet: %u, chipnet: %u, regtest: %u)", + strprintf("Set maximum mined block size in bytes (default: %u, testnet: %u, testnet4: %u," + " scalenet: %u, chipnet: %u, tempnet: %u, regtest: %u)", defaultChainParams->GetConsensus().GetDefaultGeneratedBlockSizeBytes(), testnetChainParams->GetConsensus().GetDefaultGeneratedBlockSizeBytes(), testnet4ChainParams->GetConsensus().GetDefaultGeneratedBlockSizeBytes(), scalenetChainParams->GetConsensus().GetDefaultGeneratedBlockSizeBytes(), chipnetChainParams->GetConsensus().GetDefaultGeneratedBlockSizeBytes(), + tempnetChainParams->GetConsensus().GetDefaultGeneratedBlockSizeBytes(), regtestChainParams->GetConsensus().GetDefaultGeneratedBlockSizeBytes()), ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION); gArgs.AddArg("-percentblockmaxsize=", strprintf("Set maximum mined block size as a floating-point percentage of the excessive block size." " This is an alternative to -blockmaxsize. This option and -blockmaxsize cannot both be" - " specified at the same time. (default: %.1f, testnet: %.1f, testnet4: %.1f," - " scalenet: %.3f, chipnet: %.1f, regtest: %.1f)", + " specified at the same time. (default: %.1f, testnet: %.1f, testnet4: %.1f," + " scalenet: %.3f, chipnet: %.1f, tempnet: %.1f, regtest: %.1f)", defaultChainParams->GetConsensus().nDefaultGeneratedBlockSizePercent, testnetChainParams->GetConsensus().nDefaultGeneratedBlockSizePercent, testnet4ChainParams->GetConsensus().nDefaultGeneratedBlockSizePercent, scalenetChainParams->GetConsensus().nDefaultGeneratedBlockSizePercent, chipnetChainParams->GetConsensus().nDefaultGeneratedBlockSizePercent, + tempnetChainParams->GetConsensus().nDefaultGeneratedBlockSizePercent, regtestChainParams->GetConsensus().nDefaultGeneratedBlockSizePercent), ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION); @@ -1210,12 +1233,13 @@ void SetupServerArgs() { ArgsManager::ALLOW_ANY, OptionsCategory::RPC); gArgs.AddArg("-rpcport=", strprintf("Listen for JSON-RPC connections on " - "(default: %u, testnet: %u, testnet4: %u, scalenet: %u, chipnet: %u, regtest: %u)", + "(default: %u, testnet: %u, testnet4: %u, scalenet: %u, chipnet: %u, tempnet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), testnet4BaseParams->RPCPort(), scalenetBaseParams->RPCPort(), chipnetBaseParams->RPCPort(), + tempnetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::RPC); gArgs.AddArg("-rpcallowip=", @@ -1623,6 +1647,29 @@ bool AppInitParameterInteraction(Config &config) { section.m_file, section.m_line, section.m_name)); } + if (gArgs.IsArgSet("-upgrade12activationtx")) { + const bool tempnet_requested = gArgs.IsArgSet("-tempnet"); + // After the real chipnet upgrade, -tempnet is interpreted as -chipnet. + const bool tempnetShutdown = GetTime() >= Consensus::UpgradeTimes::NOV_2025; + if (tempnet_requested && tempnetShutdown) { + InitWarning("Past tempnet shutdown, ignoring -tempnet and -upgrade12activationtx."); + } else if (chainparams.NetworkIDString() == CBaseChainParams::CHIPNET || + chainparams.NetworkIDString() == CBaseChainParams::TEMPNET) { + auto &mutableParams = const_cast(chainparams.GetConsensus()); + mutableParams.upgrade12ActivationTx = ParseHex(gArgs.GetArg("-upgrade12activationtx", "")); + CDataStream ss(mutableParams.upgrade12ActivationTx, SER_NETWORK, PROTOCOL_VERSION); + CMutableTransaction mtx; + try { + ss >> mtx; + mutableParams.upgrade12ActivationTxid = CTransaction(mtx).GetId(); + } catch (const std::exception &e) { + return InitError(strprintf("Invalid -upgrade12activationtx: %s", e.what())); + } + } else { + InitWarning("Ignoring -upgrade12activationtx: unsupported on this network."); + } + } + if (!fs::is_directory(GetBlocksDir())) { return InitError( strprintf(_("Specified blocks directory \"%s\" does not exist."), @@ -2197,10 +2244,11 @@ bool AppInitMain(Config &config, RPCServer &rpcServer, * Set up the "software outdated" mechanism. */ if (gArgs.GetBoolArg("-expire", software_outdated::DEFAULT_EXPIRE)) { - // The software outdated warning will start to happen 30 days before May 15th, 2026; - // on -chipnet this date is 30 days before Nov. 15th, 2025. - software_outdated::nTime = gArgs.GetArg("-upgrade12activationtime", - chainparams.GetConsensus().upgrade12ActivationTime); + // Software-outdated warning starts 30 days before expiry time. + const int64_t expiry_default = chainparams.GetConsensus().softwareExpiryTime; + // Not CLI-configurable: use per-network default; if unset (0), fall back to next tentative upgrade MTP. + const int64_t fallback_upgrade_time = gArgs.GetArg("-upgrade12activationtime", chainparams.GetConsensus().upgrade12ActivationTime); + software_outdated::nTime = (expiry_default != 0) ? expiry_default : fallback_upgrade_time; if (software_outdated::nTime > 0) { software_outdated::fDisableRPCOnExpiry = gArgs.GetBoolArg("-expirerpc", software_outdated::DEFAULT_EXPIRE_RPC); @@ -2215,6 +2263,18 @@ bool AppInitMain(Config &config, RPCServer &rpcServer, software_outdated::nTime = 0; } + if (chainparams.NetworkIDString() == CBaseChainParams::TEMPNET) { + // Tempnet automatically shuts down after the real chipnet upgrade. + const int64_t shutdown_time = chainparams.GetConsensus().softwareExpiryTime + 2 * 60 * 60; // +2 hours + const int64_t now = GetTime(); + const int64_t ms = std::max((shutdown_time - now) * 1000, 100); + scheduler.scheduleFromNow([] { + LogPrintf("Tempnet: auto-shutdown after upgrade grace period elapsed.\n"); + StartShutdown(); + }, ms); + LogPrintf("Tempnet: scheduling auto-shutdown at %d (ms from now: %lld)\n", shutdown_time, (long long)ms); + } + /** * Start the RPC server. It will be started in "warmup" mode and not @@ -2690,6 +2750,32 @@ bool AppInitMain(Config &config, RPCServer &rpcServer, LogPrintf(" block index %15dms\n", GetTimeMillis() - nStart); } + // Queue the network activation transaction (if configured) only if the + // upgrade has not yet activated at the current tip. If already active, + // skip queuing entirely to avoid needless re-broadcast attempts. + { + const auto ¶ms = config.GetChainParams().GetConsensus(); + if (!params.upgrade12ActivationTx.empty()) { + bool should_queue = true; + { + LOCK(cs_main); + const CBlockIndex *tip = ::ChainActive().Tip(); + if (tip && IsUpgrade12Enabled(params, tip)) { + should_queue = false; + } + } + if (should_queue) { + CDataStream ss(params.upgrade12ActivationTx, SER_NETWORK, PROTOCOL_VERSION); + CMutableTransaction mtx; + ss >> mtx; + (void)EnqueueTxForBroadcast(MakeTransactionRef(mtx), std::nullopt, + params.upgrade12ActivationTime); + } else { + LogPrint(BCLog::MEMPOOL, "Skipping queue of activation tx: upgrade already active at tip\n"); + } + } + } + // Encoded addresses using cashaddr instead of base58. // We do this by default to avoid confusion with BTC addresses. config.SetCashAddrEncoding(gArgs.GetBoolArg("-usecashaddr", DEFAULT_USE_CASHADDR)); diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp index 0d3885a2cd..2cb0db1a3c 100644 --- a/src/node/transaction.cpp +++ b/src/node/transaction.cpp @@ -18,7 +18,8 @@ #include TxId BroadcastTransaction(const Config &config, const CTransactionRef tx, - const bool allowhighfees) { + const bool allowhighfees, + const bool wait_for_wallet) { std::promise promise; const TxId &txid = tx->GetId(); @@ -58,14 +59,22 @@ TxId BroadcastTransaction(const Config &config, const CTransactionRef tx, throw JSONRPCError(RPC_TRANSACTION_ERROR, FormatStateMessage(state)); } else { - // If wallet is enabled, ensure that the wallet has been made + // If wait_for_wallet, ensure that the wallet has been made // aware of the new transaction prior to returning. This // prevents a race where a user might call sendrawtransaction // with a transaction to/from their wallet, immediately call // some wallet RPC, and get a stale result because callbacks // have not yet been processed. - CallFunctionInValidationInterfaceQueue( - [&promise] { promise.set_value(); }); + if (wait_for_wallet) { + CallFunctionInValidationInterfaceQueue( + [&promise] { promise.set_value(); }); + } else { + // Do not queue-and-wait when we're already executing on + // the validation interface queue (e.g. broadcasts + // triggered from tip updates). Skipping the wait avoids a + // deadlock of the single-threaded queue. + promise.set_value(); + } } } else if (fHaveChain) { throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, @@ -77,7 +86,9 @@ TxId BroadcastTransaction(const Config &config, const CTransactionRef tx, } } // cs_main - promise.get_future().wait(); + if (wait_for_wallet) { + promise.get_future().wait(); + } if (!g_connman) { throw JSONRPCError( diff --git a/src/node/transaction.h b/src/node/transaction.h index 17866ff085..c26b5db949 100644 --- a/src/node/transaction.h +++ b/src/node/transaction.h @@ -12,4 +12,5 @@ struct TxId; /** Broadcast a transaction */ TxId BroadcastTransaction(const Config &config, CTransactionRef tx, - bool allowhighfees = false); + bool allowhighfees = false, + bool wait_for_wallet = true); diff --git a/src/node/txbroadcastqueue.cpp b/src/node/txbroadcastqueue.cpp new file mode 100644 index 0000000000..4879ceb222 --- /dev/null +++ b/src/node/txbroadcastqueue.cpp @@ -0,0 +1,157 @@ +// Copyright (c) 2025 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 +#include +#include +#include + +#include + +namespace { +struct QueuedTx { + CTransactionRef tx; + std::optional height; + std::optional mtp; +}; + +static RecursiveMutex cs_tx_broadcast_queue; +static std::vector g_tx_broadcast_queue GUARDED_BY(cs_tx_broadcast_queue); +} // namespace + +TxId BroadcastTransaction(const Config &config, CTransactionRef tx, + bool allowhighfees = false, + bool wait_for_wallet = true); + +bool EnqueueTxForBroadcast(const CTransactionRef &tx, + std::optional height, + std::optional mtp) { + const auto txid = tx->GetId(); + if (!height && !mtp) { + LogPrint(BCLog::MEMPOOL, + "Ignoring request to queue tx %s with no height/mtp trigger\n", + txid.ToString()); + return false; + } + if (height && mtp) { + LogPrint(BCLog::MEMPOOL, + "Ignoring request to queue tx %s with both height and mtp triggers\n", + txid.ToString()); + return false; + } + if (height && *height < 0) { + LogPrint(BCLog::MEMPOOL, + "Ignoring request to queue tx %s with negative height %d\n", + txid.ToString(), *height); + return false; + } + if (mtp && *mtp < 0) { + LogPrint(BCLog::MEMPOOL, + "Ignoring request to queue tx %s with negative mtp %d\n", + txid.ToString(), *mtp); + return false; + } + LOCK(cs_tx_broadcast_queue); + for (const auto &e : g_tx_broadcast_queue) { + if (e.tx->GetId() == txid) { + LogPrint(BCLog::MEMPOOL, + "Ignoring duplicate request to queue tx %s\n", + txid.ToString()); + return false; + } + } + g_tx_broadcast_queue.push_back({tx, height, mtp}); + if (height) { + LogPrint(BCLog::MEMPOOL, + "Queued tx %s for broadcast at height %d\n", + txid.ToString(), *height); + } else if (mtp) { + LogPrint(BCLog::MEMPOOL, + "Queued tx %s for broadcast at MTP %d\n", + txid.ToString(), *mtp); + } + return true; +} + +void ProcessTxBroadcastQueue(const Config &config, const CBlockIndex *pindexNew) { + std::vector toBroadcast; + { + LOCK(cs_tx_broadcast_queue); + auto it = g_tx_broadcast_queue.begin(); + while (it != g_tx_broadcast_queue.end()) { + bool ready = false; + if (it->height && pindexNew->nHeight >= *it->height) ready = true; + if (it->mtp && pindexNew->GetMedianTimePast() >= *it->mtp) ready = true; + if (ready) { + toBroadcast.push_back(*it); + it = g_tx_broadcast_queue.erase(it); + } else { + ++it; + } + } + } + for (const auto &entry : toBroadcast) { + const CTransactionRef ptx = entry.tx; + const auto txid = ptx->GetId(); + const std::string trigger = entry.height ? strprintf("height>=%d", *entry.height) + : strprintf("mtp>=%d", *entry.mtp); + LogPrint(BCLog::MEMPOOL, + "Broadcasting queued tx %s (trigger: %s)\n", + txid.ToString(), trigger); + CallFunctionInValidationInterfaceQueue([&config, ptx, txid, trigger]() { + try { + BroadcastTransaction(config, ptx, true /*allowhighfees*/, false /*wait_for_wallet*/); + LogPrintf("Queued-broadcast success: %s (trigger: %s)\n", txid.ToString(), trigger); + } catch (const JSONRPCError &e) { + LogPrintf("Queued-broadcast failed: %s (trigger: %s) err=%s\n", + txid.ToString(), trigger, e.message); + } catch (const std::exception &e) { + LogPrintf("Queued-broadcast failed: %s (trigger: %s) err=%s\n", + txid.ToString(), trigger, e.what()); + } catch (...) { + LogPrintf("Queued-broadcast failed: %s (trigger: %s) err=%s\n", + txid.ToString(), trigger, "unknown"); + } + }); + } +} + +std::vector, std::optional, unsigned int>> +GetTxBroadcastQueue() { + std::vector, std::optional, unsigned int>> out; + LOCK(cs_tx_broadcast_queue); + out.reserve(g_tx_broadcast_queue.size()); + for (const auto &e : g_tx_broadcast_queue) { + out.emplace_back(e.tx->GetId().ToString(), e.height, e.mtp, e.tx->GetTotalSize()); + } + return out; +} + +bool CancelQueuedTxBroadcast(const uint256 &txid) { + LOCK(cs_tx_broadcast_queue); + bool removed = false; + auto it = g_tx_broadcast_queue.begin(); + while (it != g_tx_broadcast_queue.end()) { + if (it->tx->GetId() == txid) { + it = g_tx_broadcast_queue.erase(it); + removed = true; + } else { + ++it; + } + } + if (removed) { + LogPrint(BCLog::MEMPOOL, + "Cancelled queued broadcast for tx %s\n", + txid.ToString()); + } + return removed; +} diff --git a/src/node/txbroadcastqueue.h b/src/node/txbroadcastqueue.h new file mode 100644 index 0000000000..f03786e540 --- /dev/null +++ b/src/node/txbroadcastqueue.h @@ -0,0 +1,32 @@ +// Copyright (c) 2025 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +class CBlockIndex; +class Config; + +// Lightweight queue for scheduling transaction broadcasts by height or MTP. +// Node-level facility (non-consensus), in-memory only. + +bool EnqueueTxForBroadcast(const CTransactionRef &tx, + std::optional height, + std::optional mtp); + +void ProcessTxBroadcastQueue(const Config &config, const CBlockIndex *pindexNew); + +std::vector, std::optional, unsigned int>> +GetTxBroadcastQueue(); + +bool CancelQueuedTxBroadcast(const uint256 &txid); diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 8b358fa2d7..69d71522c9 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -56,3 +56,4 @@ static const int MAX_URI_LENGTH = 255; #define QAPP_APP_NAME_TESTNET4 "BitcoinCashNode-Qt-testnet4" #define QAPP_APP_NAME_SCALENET "BitcoinCashNode-Qt-scalenet" #define QAPP_APP_NAME_CHIPNET "BitcoinCashNode-Qt-chipnet" +#define QAPP_APP_NAME_TEMPNET "BitcoinCashNode-Qt-tempnet" diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index ecf92ccc51..3d28910ff7 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -595,6 +595,9 @@ static fs::path StartupShortcutPath() { if (chain == CBaseChainParams::CHIPNET) { return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin Cash Node (chipnet).lnk"; } + if (chain == CBaseChainParams::TEMPNET) { + return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin Cash Node (tempnet).lnk"; + } return GetSpecialFolderPath(CSIDL_STARTUP) / strprintf("Bitcoin Cash Node (%s).lnk", chain); // If we get here: "regtest" } diff --git a/src/qt/networkstyle.cpp b/src/qt/networkstyle.cpp index d1c1edc883..2525fea238 100644 --- a/src/qt/networkstyle.cpp +++ b/src/qt/networkstyle.cpp @@ -23,6 +23,7 @@ static const struct { {"test4", QAPP_APP_NAME_TESTNET4, 300, QT_TRANSLATE_NOOP("SplashScreen", "[testnet4]")}, {"scale", QAPP_APP_NAME_SCALENET, 240, QT_TRANSLATE_NOOP("SplashScreen", "[scalenet]")}, {"chip", QAPP_APP_NAME_CHIPNET, 60, QT_TRANSLATE_NOOP("SplashScreen", "[chipnet]")}, + {"temp", QAPP_APP_NAME_TEMPNET, 30, QT_TRANSLATE_NOOP("SplashScreen", "[tempnet]")}, {"regtest", QAPP_APP_NAME_TESTNET, 180, "[regtest]" }}; static const unsigned network_styles_count = sizeof(network_styles) / sizeof(*network_styles); diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 7fe204774f..14074e8059 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -103,9 +103,9 @@ static bool ipcCanParseLegacyURI(const QString &arg, // void PaymentServer::ipcParseCommandLine(interfaces::Node &node, int argc, char *argv[]) { - std::array networks = { + std::array networks = { {&CBaseChainParams::MAIN, &CBaseChainParams::TESTNET, &CBaseChainParams::TESTNET4, &CBaseChainParams::CHIPNET, - &CBaseChainParams::REGTEST}}; + &CBaseChainParams::TEMPNET, &CBaseChainParams::REGTEST}}; const std::string *chosenNetwork = nullptr; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 70414d1961..807172fc4b 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -28,6 +28,7 @@ #include #include #include