Skip to content
Merged
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
8 changes: 8 additions & 0 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1186,6 +1186,10 @@ void CRegTestParams::UpdateLLMQTestParametersFromArgs(const ArgsManager& args, c
if (!ParseInt32(vParams[1], &threshold)) {
throw std::runtime_error(strprintf("Invalid %s threshold (%s)", llmq_name, vParams[1]));
}
if (size > Consensus::MAX_LLMQ_SIZE) {
throw std::runtime_error(strprintf("Invalid %s size (%d), must not exceed the maximum supported quorum size (%d)",
llmq_name, size, Consensus::MAX_LLMQ_SIZE));
}
LogPrintf("Setting %s parameters to size=%ld, threshold=%ld\n", llmq_name, size, threshold);
UpdateLLMQTestParameters(size, threshold, llmqType);
}
Expand Down Expand Up @@ -1352,6 +1356,10 @@ void CDevNetParams::UpdateLLMQDevnetParametersFromArgs(const ArgsManager& args)
if (!ParseInt32(vParams[1], &threshold)) {
throw std::runtime_error(strprintf("Invalid LLMQ_DEVNET threshold (%s)", vParams[1]));
}
if (size > Consensus::MAX_LLMQ_SIZE) {
throw std::runtime_error(strprintf("Invalid LLMQ_DEVNET size (%d), must not exceed the maximum supported quorum size (%d)",
size, Consensus::MAX_LLMQ_SIZE));
}
LogPrintf("Setting LLMQ_DEVNET parameters to size=%ld, threshold=%ld\n", size, threshold);
UpdateLLMQDevnetParameters(size, threshold);
}
Expand Down
5 changes: 5 additions & 0 deletions src/llmq/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@

namespace Consensus {

//! The largest quorum size the signing protocol supports. The signing session inventory
//! and batched sig-share messages are bounded against this value, so quorum sizes above it
//! (only reachable through the devnet/regtest size overrides) cannot sign end-to-end.
constexpr int MAX_LLMQ_SIZE{400};

enum class LLMQType : uint8_t {
LLMQ_NONE = 0xff,

Expand Down
5 changes: 3 additions & 2 deletions src/llmq/signing_shares.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include <bls/bls.h>
#include <evo/types.h>
#include <llmq/params.h>
#include <llmq/signhash.h>
#include <llmq/signing.h>

Expand Down Expand Up @@ -42,8 +43,8 @@ using SigShareKey = std::pair<uint256, uint16_t>;

constexpr uint32_t UNINITIALIZED_SESSION_ID{std::numeric_limits<uint32_t>::max()};

// 400 is the maximum quorum size, so this is also the maximum number of sigs we need to support
constexpr size_t MAX_MSGS_TOTAL_BATCHED_SIGS{400};
// The maximum quorum size is also the maximum number of sigs we need to support
constexpr size_t MAX_MSGS_TOTAL_BATCHED_SIGS{Consensus::MAX_LLMQ_SIZE};

class CSigShare : virtual public CSigBase
{
Expand Down
62 changes: 62 additions & 0 deletions src/test/llmq_params_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@
#include <test/util/llmq_tests.h>
#include <test/util/setup_common.h>

#include <chainparams.h>
#include <chainparamsbase.h>
#include <consensus/params.h>
#include <llmq/params.h>
#include <util/system.h>

#include <boost/test/unit_test.hpp>

#include <algorithm>
#include <limits>
#include <memory>
#include <string>

using namespace llmq;
using namespace llmq::testutils;
Expand Down Expand Up @@ -206,4 +212,60 @@ BOOST_AUTO_TEST_CASE(llmq_params_calculations_overflow_test)
BOOST_CHECK_EQUAL(cycles, 0); // 1000 / max_int = 0
}

//! Build chain params for `chain` with a single LLMQ size/threshold override applied.
static std::unique_ptr<const CChainParams> ChainParamsWithLLMQOverride(const std::string& chain,
const std::string& arg,
int size, int threshold)
{
ArgsManager args;
args.ForceSetArg(arg, strprintf("%d:%d", size, threshold));
return CreateChainParams(args, chain);
}

static int LLMQSize(const CChainParams& params, LLMQType type)
{
const auto& llmqs = params.GetConsensus().llmqs;
const auto it = std::ranges::find_if(llmqs, [type](const auto& llmq) { return llmq.type == type; });
BOOST_REQUIRE(it != llmqs.end());
return it->size;
}

// Quorum sizes above MAX_LLMQ_SIZE cannot sign end-to-end: signing session inventories and
// batched sig-share messages are bounded by that same maximum, so a node running such a quorum
// would drop its peers' sig shares. Reject the override at chain-parameter construction instead.
BOOST_AUTO_TEST_CASE(llmq_params_size_override_bounds_test)
{
constexpr int kMax = Consensus::MAX_LLMQ_SIZE;

const std::pair<std::string, LLMQType> regtest_overrides[]{
{"-llmqtestparams", LLMQType::LLMQ_TEST},
{"-llmqtestinstantsendparams", LLMQType::LLMQ_TEST_INSTANTSEND},
{"-llmqtestplatformparams", LLMQType::LLMQ_TEST_PLATFORM},
};

for (const auto& [arg, type] : regtest_overrides) {
// Normal custom values keep working
const auto small = ChainParamsWithLLMQOverride(CBaseChainParams::REGTEST, arg, 5, 3);
BOOST_CHECK_EQUAL(LLMQSize(*small, type), 5);

// Exactly the maximum is accepted
const auto at_max = ChainParamsWithLLMQOverride(CBaseChainParams::REGTEST, arg, kMax, kMax * 2 / 3);
BOOST_CHECK_EQUAL(LLMQSize(*at_max, type), kMax);

// One above the maximum is rejected
BOOST_CHECK_THROW(ChainParamsWithLLMQOverride(CBaseChainParams::REGTEST, arg, kMax + 1, kMax * 2 / 3),
std::runtime_error);
}

// Same bounds for the devnet override
const auto devnet_small = ChainParamsWithLLMQOverride(CBaseChainParams::DEVNET, "-llmqdevnetparams", 12, 6);
BOOST_CHECK_EQUAL(LLMQSize(*devnet_small, LLMQType::LLMQ_DEVNET), 12);

const auto devnet_at_max = ChainParamsWithLLMQOverride(CBaseChainParams::DEVNET, "-llmqdevnetparams", kMax, kMax / 2);
BOOST_CHECK_EQUAL(LLMQSize(*devnet_at_max, LLMQType::LLMQ_DEVNET), kMax);

BOOST_CHECK_THROW(ChainParamsWithLLMQOverride(CBaseChainParams::DEVNET, "-llmqdevnetparams", kMax + 1, kMax / 2),
std::runtime_error);
}

BOOST_AUTO_TEST_SUITE_END()