Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 58 additions & 1 deletion src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@

#include <chainparamsseeds.h>
#include <consensus/merkle.h>
#include <crypto/sha256.h>
#include <deploymentinfo.h>
#include <hash.h> // for signet block challenge hash
#include <issuance.h>
#include <primitives/transaction.h>
#include <util/moneystr.h>
#include <util/system.h>
#include <crypto/sha256.h>

#include <assert.h>

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<int>::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<CAmount> 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<int>::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<CAmount> 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.
*/
Expand Down Expand Up @@ -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<unsigned char> commit = CommitToArguments(consensus, strNetworkID);
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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", ""));
Expand Down
25 changes: 25 additions & 0 deletions src/chainparams.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,27 @@ struct CCheckpointData {
}
};

// ELEMENTS
struct PeginSubsidy {
int height{std::numeric_limits<int>::max()};
CAmount threshold{0};

PeginSubsidy() {};
bool IsDefined() {
return threshold > 0 || height < std::numeric_limits<int>::max();
};
};

struct PeginMinimum {
int height{std::numeric_limits<int>::max()};
CAmount amount{0};

PeginMinimum() {};
bool IsDefined() {
return amount > 0 || height < std::numeric_limits<int>::max();
};
};

struct AssumeutxoHash : public BaseHash<uint256> {
explicit AssumeutxoHash(const uint256& hash) : BaseHash(hash) {}
};
Expand Down Expand Up @@ -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() {}
Expand Down Expand Up @@ -173,6 +196,8 @@ class CChainParams
bool multi_data_permitted;
bool accept_discount_ct;
bool create_discount_ct;
PeginSubsidy pegin_subsidy;
PeginMinimum pegin_minimum;
};

/**
Expand Down
31 changes: 31 additions & 0 deletions src/dynafed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<unsigned char> 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;
}
5 changes: 5 additions & 0 deletions src/dynafed.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
21 changes: 21 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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=<mode>", "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);
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/pegins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
20 changes: 20 additions & 0 deletions src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include <txdb.h>
#include <txmempool.h>
#include <undo.h>
#include <util/moneystr.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/translation.h>
Expand Down Expand Up @@ -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<int>::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<int>::max()) {
obj.pushKV("pegin_subsidy_height", pegin_subsidy.height);
obj.pushKV("pegin_subsidy_active", chainman.ActiveTip()->nHeight >= pegin_subsidy.height);
}

return obj;
},
};
Expand Down
2 changes: 2 additions & 0 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
49 changes: 46 additions & 3 deletions src/test/dynafed_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <test/util/setup_common.h>
#include <string>
#include <boost/test/unit_test.hpp>
#include <dynafed.h>
#include <primitives/block.h>
#include <script/script.h>
#include <serialize.h>
#include <string>
#include <test/util/setup_common.h>


BOOST_FIXTURE_TEST_SUITE(dynafed_tests, BasicTestingSetup)
Expand Down Expand Up @@ -41,6 +42,48 @@ BOOST_AUTO_TEST_CASE(dynafed_params_root)
);
}

BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(parse_fedpegscript_multisig)
{
int t = 0;
int n = 0;

auto simplebytes = ParseHex("512103dff4923d778550cc13ce0d887d737553b4b58f4e8e886507fc39f5e447b2186451ae");

CScript simple{simplebytes.begin(), simplebytes.end()};

BOOST_CHECK(ParseFedPegQuorum(simple, t, n));
BOOST_CHECK_EQUAL(t, 1);
BOOST_CHECK_EQUAL(n, 1);

auto liquidv1bytes = ParseHex("5b21020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b678172612102675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af992102896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d4821029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c2102a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc401021031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb2103079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b2103111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2210318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa08401742103230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de121035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a62103bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c2103cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d175462103d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d4248282103ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a5fae736402c00fb269522103aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79210291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807210386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb53ae68");

CScript liquidv1{liquidv1bytes.begin(), liquidv1bytes.end()};

t = 0;
n = 0;
BOOST_CHECK(ParseFedPegQuorum(liquidv1, t, n));
BOOST_CHECK_EQUAL(t, 11);
BOOST_CHECK_EQUAL(n, 15);

auto optruebytes = ParseHex("51");

CScript optrue{optruebytes.begin(), optruebytes.end()};

t = 0;
n = 0;
BOOST_CHECK(ParseFedPegQuorum(optrue, t, n));
BOOST_CHECK_EQUAL(t, 1);
BOOST_CHECK_EQUAL(n, 0);

auto op3bytes = ParseHex("53");

CScript op3{op3bytes.begin(), op3bytes.end()};

t = 0;
n = 0;
BOOST_CHECK(ParseFedPegQuorum(op3, t, n));
BOOST_CHECK_EQUAL(t, 3);
BOOST_CHECK_EQUAL(n, 0);
}

BOOST_AUTO_TEST_SUITE_END()
Loading