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 @@ class CMainParams : public CChainParams { 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 @@ class CTestNetParams : public CChainParams { 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 @@ class SigNetParams : public CChainParams { 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 @@ class CRegTestParams : public CChainParams { 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 @@ class CCustomParams : public CRegTestParams { 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 @@ class CLiquidV1Params : public CChainParams { 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 @@ class CLiquidV1TestParams : public CLiquidV1Params { 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 @@ class CChainParams 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 @@ class CChainParams bool multi_data_permitted; bool accept_discount_ct; bool create_discount_ct; + PeginSubsidy pegin_subsidy; + PeginMinimum pegin_minimum; }; /** diff --git a/src/dynafed.cpp b/src/dynafed.cpp index d719795149..cb288a83ed 100644 --- a/src/dynafed.cpp +++ b/src/dynafed.cpp @@ -123,3 +123,34 @@ DynaFedParamEntry ComputeNextBlockCurrentParameters(const CBlockIndex* pindexPre } } +bool ParseFedPegQuorum(const CScript& fedpegscript, int& t, int& n) { + CScript::const_iterator it = fedpegscript.begin(); + std::vector vch; + opcodetype opcode; + + // parse the required threshold number + if (!fedpegscript.GetOp(it, opcode, vch)) return false; + t = CScript::DecodeOP_N(opcode); + if (t < 1 || t > MAX_PUBKEYS_PER_MULTISIG) return false; + + // support a fedpegscript like OP_TRUE if we're at the end of the script + if (it == fedpegscript.end()) return true; + + // count the pubkeys + int pubkeys = 0; + while (fedpegscript.GetOp(it, opcode, vch)) { + if (opcode != 0x21) break; + if (vch.size() != 33) return false; + pubkeys++; + } + + // parse the total number of pubkeys + n = CScript::DecodeOP_N(opcode); + if (n < 1 || n > MAX_PUBKEYS_PER_MULTISIG || n < t) return false; + if (pubkeys != n) return false; + + // the next opcode must be OP_CHECKMULTISIG + if (!fedpegscript.GetOp(it, opcode, vch)) return false; + + return opcode == OP_CHECKMULTISIG; +} diff --git a/src/dynafed.h b/src/dynafed.h index ea651631fb..373fdb2e97 100644 --- a/src/dynafed.h +++ b/src/dynafed.h @@ -15,5 +15,10 @@ DynaFedParamEntry ComputeNextBlockFullCurrentParameters(const CBlockIndex* pinde * publish signblockscript-related fields */ DynaFedParamEntry ComputeNextBlockCurrentParameters(const CBlockIndex* pindexPrev, const Consensus::Params& consensus); +/* Get the threshold (t) and maybe the total pubkeys (n) of the first OP_CHECKMULTISIG in the fedpegscript. + * Assumes the fedpegscript starts with the threshold, otherwise returns false. + * Uses CScript::DecodeOP_N, so only supports up to a threshold of 16, otherwise asserts. + * Supports a fedpegscript like OP_TRUE by returning early. */ +bool ParseFedPegQuorum(const CScript& fedpegscript, int& t, int& n); #endif // BITCOIN_DYNAFED_H diff --git a/src/init.cpp b/src/init.cpp index 27addf275a..f2bbc8ed90 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). This is an OP_RETURN output with value of the parent transaction feerate times the cost of spending the WSH output (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 diff --git a/src/pegins.cpp b/src/pegins.cpp index 72551d2c3b..8e44936398 100644 --- a/src/pegins.cpp +++ b/src/pegins.cpp @@ -577,7 +577,7 @@ bool DecomposePeginWitness(const CScriptWitness& witness, CAmount& value, CAsset tx = elem_tx; } - CDataStream ss_proof(stack[5], SER_NETWORK, PROTOCOL_VERSION); + CDataStream ss_proof(stack[5], SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS); if (Params().GetConsensus().ParentChainHasPow()) { Sidechain::Bitcoin::CMerkleBlock tx_proof; ss_proof >> tx_proof; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index b20765a9e2..602cd5d952 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -3134,6 +3135,25 @@ static RPCHelpMan getsidechaininfo() obj.pushKV("parent_chain_signblockscript_hex", HexStr(consensus.parent_chain_signblockscript)); obj.pushKV("parent_pegged_asset", consensus.parent_pegged_asset.GetHex()); } + + PeginMinimum pegin_minimum = Params().GetPeginMinimum(); + if (pegin_minimum.amount > 0) { + obj.pushKV("pegin_min_amount", FormatMoney(pegin_minimum.amount)); + } + if (pegin_minimum.height < std::numeric_limits::max()) { + obj.pushKV("pegin_min_height", pegin_minimum.height); + obj.pushKV("pegin_min_active", chainman.ActiveTip()->nHeight >= pegin_minimum.height); + } + + PeginSubsidy pegin_subsidy = Params().GetPeginSubsidy(); + if (pegin_subsidy.threshold > 0) { + obj.pushKV("pegin_subsidy_threshold", FormatMoney(pegin_subsidy.threshold)); + } + if (pegin_subsidy.height < std::numeric_limits::max()) { + obj.pushKV("pegin_subsidy_height", pegin_subsidy.height); + obj.pushKV("pegin_subsidy_active", chainman.ActiveTip()->nHeight >= pegin_subsidy.height); + } + return obj; }, }; diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 5d8da16722..3b65133921 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -239,6 +239,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "calculateasset", 3, "blind_reissuance" }, { "updatepsbtpegin", 1, "input" }, { "updatepsbtpegin", 2, "value" }, + { "claimpegin", 3, "fee_rate" }, + { "createrawpegin", 3, "fee_rate" }, }; // clang-format on diff --git a/src/test/dynafed_tests.cpp b/src/test/dynafed_tests.cpp index 3a186db51c..f9a8099ae1 100644 --- a/src/test/dynafed_tests.cpp +++ b/src/test/dynafed_tests.cpp @@ -2,12 +2,13 @@ // 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