From 94cde59dc3d17ff4b87e7aa1e1eadb96cde899fe Mon Sep 17 00:00:00 2001 From: Byron Hambly Date: Wed, 22 Oct 2025 15:53:12 +0200 Subject: [PATCH] subsidy: add chainparams and init --- src/chainparams.cpp | 59 ++++++++++++++++++++++++++++++++++++++++++++- src/chainparams.h | 25 +++++++++++++++++++ src/init.cpp | 21 ++++++++++++++++ 3 files changed, 104 insertions(+), 1 deletion(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index a2b4c418af..e3490d57f8 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -7,12 +7,13 @@ #include #include +#include #include #include // for signet block challenge hash #include #include +#include #include -#include #include @@ -232,6 +233,8 @@ public: multi_data_permitted = false; accept_discount_ct = false; create_discount_ct = false; + pegin_subsidy = PeginSubsidy(); + pegin_minimum = PeginMinimum(); consensus.has_parent_chain = false; g_signed_blocks = false; g_con_elementsmode = false; @@ -379,6 +382,8 @@ public: multi_data_permitted = false; accept_discount_ct = false; create_discount_ct = false; + pegin_subsidy = PeginSubsidy(); + pegin_minimum = PeginMinimum(); consensus.has_parent_chain = false; g_signed_blocks = false; g_con_elementsmode = false; @@ -544,6 +549,8 @@ public: multi_data_permitted = false; accept_discount_ct = false; create_discount_ct = false; + pegin_subsidy = PeginSubsidy(); + pegin_minimum = PeginMinimum(); consensus.has_parent_chain = false; g_signed_blocks = false; // lol g_con_elementsmode = false; @@ -648,6 +655,8 @@ public: multi_data_permitted = false; accept_discount_ct = false; create_discount_ct = false; + pegin_subsidy = PeginSubsidy(); + pegin_minimum = PeginMinimum(); consensus.has_parent_chain = false; g_signed_blocks = false; g_con_elementsmode = false; @@ -792,6 +801,39 @@ void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args) } } +// ELEMENTS +PeginSubsidy ParsePeginSubsidy(const ArgsManager& args) { + PeginSubsidy pegin_subsidy; + + pegin_subsidy.height = args.GetIntArg("-peginsubsidyheight", std::numeric_limits::max()); + if (pegin_subsidy.height < 0) { + throw std::runtime_error(strprintf("Invalid block height (%d) for -peginsubsidyheight. Must be positive.", pegin_subsidy.height)); + } + if (std::optional amount = ParseMoney(args.GetArg("-peginsubsidythreshold", "0"))) { + pegin_subsidy.threshold = amount.value(); + } else { + throw std::runtime_error("Invalid -peginsubsidythreshold"); + } + + return pegin_subsidy; +}; + +PeginMinimum ParsePeginMinimum(const ArgsManager& args) { + PeginMinimum pegin_minimum; + + pegin_minimum.height = args.GetIntArg("-peginminheight", std::numeric_limits::max()); + if (pegin_minimum.height < 0) { + throw std::runtime_error(strprintf("Invalid block height (%d) for -peginminheight. Must be positive.", pegin_minimum.height)); + } + if (std::optional amount = ParseMoney(args.GetArg("-peginminamount", "0"))) { + pegin_minimum.amount = amount.value(); + } else { + throw std::runtime_error("Invalid -peginminamount"); + } + + return pegin_minimum; +}; + /** * Custom params for testing. */ @@ -932,6 +974,11 @@ protected: consensus.start_p2wsh_script = args.GetIntArg("-con_start_p2wsh_script", consensus.start_p2wsh_script); create_discount_ct = args.GetBoolArg("-creatediscountct", create_discount_ct); accept_discount_ct = args.GetBoolArg("-acceptdiscountct", accept_discount_ct) || create_discount_ct; + pegin_subsidy = ParsePeginSubsidy(args); + pegin_minimum = ParsePeginMinimum(args); + if (pegin_subsidy.threshold < pegin_minimum.amount) { + throw std::runtime_error(strprintf("Pegin subsidy threshold (%s) must be greater than or equal to pegin minimum amount (%s)", FormatMoney(pegin_subsidy.threshold), FormatMoney(pegin_minimum.amount))); + } // Calculate pegged Bitcoin asset std::vector commit = CommitToArguments(consensus, strNetworkID); @@ -1178,6 +1225,11 @@ public: multi_data_permitted = true; create_discount_ct = args.GetBoolArg("-creatediscountct", false); accept_discount_ct = args.GetBoolArg("-acceptdiscountct", true) || create_discount_ct; + pegin_subsidy = ParsePeginSubsidy(args); + pegin_minimum = ParsePeginMinimum(args); + if (pegin_subsidy.threshold < pegin_minimum.amount) { + throw std::runtime_error(strprintf("Pegin subsidy threshold (%s) must be greater than or equal to pegin minimum amount (%s)", FormatMoney(pegin_subsidy.threshold), FormatMoney(pegin_minimum.amount))); + } parentGenesisBlockHash = uint256S("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); const bool parent_genesis_is_null = parentGenesisBlockHash == uint256(); @@ -1538,6 +1590,11 @@ public: multi_data_permitted = args.GetBoolArg("-multi_data_permitted", multi_data_permitted); create_discount_ct = args.GetBoolArg("-creatediscountct", create_discount_ct); accept_discount_ct = args.GetBoolArg("-acceptdiscountct", accept_discount_ct) || create_discount_ct; + pegin_subsidy = ParsePeginSubsidy(args); + pegin_minimum = ParsePeginMinimum(args); + if (pegin_subsidy.threshold < pegin_minimum.amount) { + throw std::runtime_error(strprintf("Pegin subsidy threshold (%s) must be greater than or equal to pegin minimum amount (%s)", FormatMoney(pegin_subsidy.threshold), FormatMoney(pegin_minimum.amount))); + } if (args.IsArgSet("-parentgenesisblockhash")) { parentGenesisBlockHash = uint256S(args.GetArg("-parentgenesisblockhash", "")); diff --git a/src/chainparams.h b/src/chainparams.h index 840a22ba5c..3e318577ec 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -28,6 +28,27 @@ struct CCheckpointData { } }; +// ELEMENTS +struct PeginSubsidy { + int height{std::numeric_limits::max()}; + CAmount threshold{0}; + + PeginSubsidy() {}; + bool IsDefined() { + return threshold > 0 || height < std::numeric_limits::max(); + }; +}; + +struct PeginMinimum { + int height{std::numeric_limits::max()}; + CAmount amount{0}; + + PeginMinimum() {}; + bool IsDefined() { + return amount > 0 || height < std::numeric_limits::max(); + }; +}; + struct AssumeutxoHash : public BaseHash { explicit AssumeutxoHash(const uint256& hash) : BaseHash(hash) {} }; @@ -138,6 +159,8 @@ public: bool GetMultiDataPermitted() const { return multi_data_permitted; } bool GetAcceptDiscountCT() const { return accept_discount_ct; } bool GetCreateDiscountCT() const { return create_discount_ct; } + PeginSubsidy GetPeginSubsidy() const { return pegin_subsidy; } + PeginMinimum GetPeginMinimum() const { return pegin_minimum; } protected: CChainParams() {} @@ -173,6 +196,8 @@ protected: bool multi_data_permitted; bool accept_discount_ct; bool create_discount_ct; + PeginSubsidy pegin_subsidy; + PeginMinimum pegin_minimum; }; /** diff --git a/src/init.cpp b/src/init.cpp index 27addf275a..d1d8db2bf9 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -642,6 +642,10 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-ct_exponent", strprintf("The hiding exponent. (default: %s)", 0), ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-acceptdiscountct", "Accept discounted fees for Confidential Transactions (default: 1 in liquidtestnet and liquidv1, 0 otherwise)", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-creatediscountct", "Create Confidential Transactions with discounted fees (default: 0). Setting this to 1 will also set 'acceptdiscountct' to 1.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); + argsman.AddArg("-peginsubsidyheight", "The block height at which peg-in transactions must have a burn subsidy (default: not active). The subsidy is an OP_RETURN output, with its value equal to the feerate of the parent transaction multiplied by the vsize of spending the P2WSH output created by the peg-in (feerate * 396 sats for liquidv1). ", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); + argsman.AddArg("-peginsubsidythreshold", "The output value below which peg-in transactions must have a burn subsidy (default: 0). Peg-ins above this value do not require the subsidy.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); + argsman.AddArg("-peginminheight", "The block height at which a minimum peg-in value is enforced (default: not active).", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); + argsman.AddArg("-peginminamount", "The minimum value for a peg-in transaction after peginminheight (default: unset).", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); #if defined(USE_SYSCALL_SANDBOX) argsman.AddArg("-sandbox=", "Use the experimental syscall sandbox in the specified mode (-sandbox=log-and-abort or -sandbox=abort). Allow only expected syscalls to be used by bitcoind. Note that this is an experimental new feature that may cause bitcoind to exit or crash unexpectedly: use with caution. In the \"log-and-abort\" mode the invocation of an unexpected syscall results in a debug handler being invoked which will log the incident and terminate the program (without executing the unexpected syscall). In the \"abort\" mode the invocation of an unexpected syscall results in the entire process being killed immediately by the kernel without executing the unexpected syscall.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); @@ -1975,6 +1979,23 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) gArgs.SoftSetArg("-validatepegin", "0"); } } + // if we are validating pegin subsidy or minimum then we require bitcoind >= v25 + if (Params().GetPeginSubsidy().IsDefined() || Params().GetPeginMinimum().IsDefined()) { + UniValue params(UniValue::VARR); + UniValue reply = CallMainChainRPC("getnetworkinfo", params); + if (reply["error"].isStr()) { + InitError(Untranslated(reply["error"].get_str())); + return false; + } else { + const int version = reply["result"]["version"].get_int(); + const std::string& subversion = reply["result"]["subversion"].get_str(); + if (version < 250000 && subversion.find("Satoshi") != std::string::npos) { + const std::string err = strprintf("ERROR: parent bitcoind must be version 25 or newer for pegin subsidy/minimum validation. Found version: %s", version); + InitError(Untranslated(err)); + return false; + } + } + } } // Call ActivateBestChain every 30 seconds. This is almost always a