Merge datacarriercost-28+knots

This commit is contained in:
Luke Dashjr 2025-03-05 03:27:08 +00:00
commit 42ecf3bfb7
18 changed files with 82 additions and 12 deletions

View File

@ -21,7 +21,7 @@ static void AddTx(const CTransactionRef& tx, const CAmount& nFee, CTxMemPool& po
pool.addUnchecked(CTxMemPoolEntry( pool.addUnchecked(CTxMemPoolEntry(
tx, nFee, nTime, dPriority, nHeight, sequence, tx, nFee, nTime, dPriority, nHeight, sequence,
tx->GetValueOut(), tx->GetValueOut(),
spendsCoinbase, sigOpCost, lp)); spendsCoinbase, /*extra_weight=*/0, sigOpCost, lp));
} }
// Right now this is only testing eviction performance in an extremely small // Right now this is only testing eviction performance in an extremely small

View File

@ -22,7 +22,7 @@ static void AddTx(const CTransactionRef& tx, CTxMemPool& pool) EXCLUSIVE_LOCKS_R
bool spendsCoinbase = false; bool spendsCoinbase = false;
unsigned int sigOpCost = 4; unsigned int sigOpCost = 4;
LockPoints lp; LockPoints lp;
pool.addUnchecked(CTxMemPoolEntry(tx, 1000, nTime, nHeight, sequence, /*entry_tx_inputs_coin_age=*/coin_age, tx->GetValueOut(), spendsCoinbase, sigOpCost, lp)); pool.addUnchecked(CTxMemPoolEntry(tx, 1000, nTime, nHeight, sequence, /*entry_tx_inputs_coin_age=*/coin_age, tx->GetValueOut(), spendsCoinbase, /*extra_weight=*/0, sigOpCost, lp));
} }
struct Available { struct Available {

View File

@ -17,7 +17,7 @@
static void AddTx(const CTransactionRef& tx, const CAmount& fee, CTxMemPool& pool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs) static void AddTx(const CTransactionRef& tx, const CAmount& fee, CTxMemPool& pool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs)
{ {
LockPoints lp; LockPoints lp;
pool.addUnchecked(CTxMemPoolEntry(tx, fee, /*time=*/0, /*entry_height=*/0, /*entry_sequence=*/0, /*entry_tx_inputs_coin_age=*/0.0, /*in_chain_input_value=*/0, /*spends_coinbase=*/false, /*sigops_cost=*/4, lp)); pool.addUnchecked(CTxMemPoolEntry(tx, fee, /*time=*/0, /*entry_height=*/0, /*entry_sequence=*/0, /*entry_tx_inputs_coin_age=*/0.0, /*in_chain_input_value=*/0, /*spends_coinbase=*/false, /*extra_weight=*/0, /*sigops_cost=*/4, lp));
} }
static void RpcMempool(benchmark::Bench& bench) static void RpcMempool(benchmark::Bench& bench)

View File

@ -507,6 +507,25 @@ int64_t SettingToInt(const common::SettingsValue& value, int64_t nDefault)
return SettingToInt(value).value_or(nDefault); return SettingToInt(value).value_or(nDefault);
} }
std::optional<int64_t> ArgsManager::GetFixedPointArg(const std::string& arg, int decimals) const
{
const common::SettingsValue value = GetSetting(arg);
return SettingToFixedPoint(value, decimals);
}
std::optional<int64_t> SettingToFixedPoint(const common::SettingsValue& value, int decimals)
{
if (value.isNull()) return std::nullopt;
if (value.isFalse()) return 0;
if (value.isTrue()) return 1;
if (!value.isNum()) value.get_str(); // throws an exception if type is wrong
int64_t v;
if (!ParseFixedPoint(value.getValStr(), decimals, &v)) {
throw std::runtime_error(strprintf("Parse error ('%s')", value.getValStr()));
}
return v;
}
bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const
{ {
return GetBoolArg(strArg).value_or(fDefault); return GetBoolArg(strArg).value_or(fDefault);

View File

@ -92,6 +92,8 @@ std::optional<std::string> SettingToString(const common::SettingsValue&);
int64_t SettingToInt(const common::SettingsValue&, int64_t); int64_t SettingToInt(const common::SettingsValue&, int64_t);
std::optional<int64_t> SettingToInt(const common::SettingsValue&); std::optional<int64_t> SettingToInt(const common::SettingsValue&);
std::optional<int64_t> SettingToFixedPoint(const common::SettingsValue&, int decimals);
bool SettingToBool(const common::SettingsValue&, bool); bool SettingToBool(const common::SettingsValue&, bool);
std::optional<bool> SettingToBool(const common::SettingsValue&); std::optional<bool> SettingToBool(const common::SettingsValue&);
@ -305,6 +307,15 @@ protected:
int64_t GetIntArg(const std::string& strArg, int64_t nDefault) const; int64_t GetIntArg(const std::string& strArg, int64_t nDefault) const;
std::optional<int64_t> GetIntArg(const std::string& strArg) const; std::optional<int64_t> GetIntArg(const std::string& strArg) const;
/**
* Return fixed-point argument
*
* @param arg Argument to get (e.g. "-foo")
* @param decimals Number of fractional decimal digits to accept
* @return Command-line argument (0 if invalid number) multiplied by 10**decimals
*/
std::optional<int64_t> GetFixedPointArg(const std::string& arg, int decimals) const;
/** /**
* Return boolean argument or default value * Return boolean argument or default value
* *

View File

@ -680,6 +680,7 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); argsman.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-bytespersigopstrict", strprintf("Minimum bytes per sigop in transactions we relay and mine (default: %u)", DEFAULT_BYTES_PER_SIGOP_STRICT), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); argsman.AddArg("-bytespersigopstrict", strprintf("Minimum bytes per sigop in transactions we relay and mine (default: %u)", DEFAULT_BYTES_PER_SIGOP_STRICT), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); argsman.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-datacarriercost", strprintf("Treat extra data in transactions as at least N vbytes per actual byte (default: %s)", DEFAULT_WEIGHT_PER_DATA_BYTE / 4.0), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-datacarrierfullcount", strprintf("Apply datacarriersize limit to all known datacarrier methods (default: %u)", DEFAULT_DATACARRIER_FULLCOUNT), ArgsManager::ALLOW_ANY | (DEFAULT_DATACARRIER_FULLCOUNT ? uint32_t{ArgsManager::DEBUG_ONLY} : 0), OptionsCategory::NODE_RELAY); argsman.AddArg("-datacarrierfullcount", strprintf("Apply datacarriersize limit to all known datacarrier methods (default: %u)", DEFAULT_DATACARRIER_FULLCOUNT), ArgsManager::ALLOW_ANY | (DEFAULT_DATACARRIER_FULLCOUNT ? uint32_t{ArgsManager::DEBUG_ONLY} : 0), OptionsCategory::NODE_RELAY);
argsman.AddArg("-datacarriersize", argsman.AddArg("-datacarriersize",
strprintf("Maximum size of data in data carrier transactions we relay and mine, in bytes (default: %u)", strprintf("Maximum size of data in data carrier transactions we relay and mine, in bytes (default: %u)",
@ -1087,6 +1088,10 @@ bool AppInitParameterInteraction(const ArgsManager& args)
} }
} }
if (auto parsed = args.GetFixedPointArg("-datacarriercost", 2)) {
g_weight_per_data_byte = ((*parsed * WITNESS_SCALE_FACTOR) + 99) / 100;
}
nBytesPerSigOp = args.GetIntArg("-bytespersigop", nBytesPerSigOp); nBytesPerSigOp = args.GetIntArg("-bytespersigop", nBytesPerSigOp);
nBytesPerSigOpStrict = args.GetIntArg("-bytespersigopstrict", nBytesPerSigOpStrict); nBytesPerSigOpStrict = args.GetIntArg("-bytespersigopstrict", nBytesPerSigOpStrict);

View File

@ -96,6 +96,7 @@ private:
const int64_t nTime; //!< Local time when entering the mempool const int64_t nTime; //!< Local time when entering the mempool
const uint64_t entry_sequence; //!< Sequence number used to determine whether this transaction is too recent for relay const uint64_t entry_sequence; //!< Sequence number used to determine whether this transaction is too recent for relay
const int64_t sigOpCost; //!< Total sigop cost const int64_t sigOpCost; //!< Total sigop cost
const int32_t m_extra_weight; //!< Policy-only additional transaction weight beyond nTxWeight
const size_t nModSize; //!< Cached modified size for priority const size_t nModSize; //!< Cached modified size for priority
const double entryPriority; //!< Priority when entering the mempool const double entryPriority; //!< Priority when entering the mempool
const unsigned int entryHeight; //!< Chain height when entering the mempool const unsigned int entryHeight; //!< Chain height when entering the mempool
@ -127,6 +128,7 @@ public:
double entry_tx_inputs_coin_age, double entry_tx_inputs_coin_age,
CAmount in_chain_input_value, CAmount in_chain_input_value,
bool spends_coinbase, bool spends_coinbase,
int32_t extra_weight,
int64_t sigops_cost, LockPoints lp) int64_t sigops_cost, LockPoints lp)
: tx{tx}, : tx{tx},
nFee{fee}, nFee{fee},
@ -135,6 +137,7 @@ public:
nTime{time}, nTime{time},
entry_sequence{entry_sequence}, entry_sequence{entry_sequence},
sigOpCost{sigops_cost}, sigOpCost{sigops_cost},
m_extra_weight{extra_weight},
nModSize{CalculateModifiedSize(*tx, GetTxSize())}, nModSize{CalculateModifiedSize(*tx, GetTxSize())},
entryPriority{ComputePriority2(entry_tx_inputs_coin_age, nModSize)}, entryPriority{ComputePriority2(entry_tx_inputs_coin_age, nModSize)},
entryHeight{entry_height}, entryHeight{entry_height},
@ -177,7 +180,7 @@ public:
const CAmount& GetFee() const { return nFee; } const CAmount& GetFee() const { return nFee; }
int32_t GetTxSize() const int32_t GetTxSize() const
{ {
return GetVirtualTransactionSize(nTxWeight, sigOpCost, ::nBytesPerSigOp); return GetVirtualTransactionSize(nTxWeight + m_extra_weight, sigOpCost, ::nBytesPerSigOp);
} }
int32_t GetTxWeight() const { return nTxWeight; } int32_t GetTxWeight() const { return nTxWeight; }
std::chrono::seconds GetTime() const { return std::chrono::seconds{nTime}; } std::chrono::seconds GetTime() const { return std::chrono::seconds{nTime}; }

View File

@ -749,7 +749,7 @@ public:
{ {
if (!m_node.mempool) return {}; if (!m_node.mempool) return {};
LockPoints lp; LockPoints lp;
CTxMemPoolEntry entry(tx, 0, 0, 0, 0, 0, 0, false, 0, lp); CTxMemPoolEntry entry(tx, 0, 0, 0, 0, 0, 0, false, /*extra_weight=*/0, 0, lp);
LOCK(m_node.mempool->cs); LOCK(m_node.mempool->cs);
return m_node.mempool->CheckPackageLimits({tx}, entry.GetTxSize()); return m_node.mempool->CheckPackageLimits({tx}, entry.GetTxSize());
} }

View File

@ -5,6 +5,7 @@
#include <coins.h> #include <coins.h>
#include <consensus/amount.h> #include <consensus/amount.h>
#include <consensus/tx_verify.h> #include <consensus/tx_verify.h>
#include <consensus/validation.h>
#include <node/psbt.h> #include <node/psbt.h>
#include <policy/policy.h> #include <policy/policy.h>
#include <policy/settings.h> #include <policy/settings.h>
@ -137,7 +138,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
if (success) { if (success) {
CTransaction ctx = CTransaction(mtx); CTransaction ctx = CTransaction(mtx);
size_t size(GetVirtualTransactionSize(ctx, GetTransactionSigOpCost(ctx, view, STANDARD_SCRIPT_VERIFY_FLAGS), ::nBytesPerSigOp)); size_t size(GetVirtualTransactionSize(GetTransactionWeight(ctx) + CalculateExtraTxWeight(ctx, view, ::g_weight_per_data_byte), GetTransactionSigOpCost(ctx, view, STANDARD_SCRIPT_VERIFY_FLAGS), ::nBytesPerSigOp));
result.estimated_vsize = size; result.estimated_vsize = size;
// Estimate fee rate // Estimate fee rate
CFeeRate feerate(fee, size); CFeeRate feerate(fee, size);

View File

@ -435,3 +435,26 @@ size_t DatacarrierBytes(const CTransaction& tx, const CCoinsViewCache& view)
return ret; return ret;
} }
int32_t CalculateExtraTxWeight(const CTransaction& tx, const CCoinsViewCache& view, const unsigned int weight_per_data_byte)
{
int32_t mod_weight{0};
// Add in any extra weight for data bytes
if (weight_per_data_byte > 1) {
for (const CTxIn& txin : tx.vin) {
const CTxOut &utxo = view.AccessCoin(txin.prevout).out;
auto[script, consensus_weight_per_byte] = GetScriptForTransactionInput(utxo.scriptPubKey, txin);
if (weight_per_data_byte > consensus_weight_per_byte) {
mod_weight += script.DatacarrierBytes() * (weight_per_data_byte - consensus_weight_per_byte);
}
}
if (weight_per_data_byte > WITNESS_SCALE_FACTOR) {
for (const CTxOut& txout : tx.vout) {
mod_weight += txout.scriptPubKey.DatacarrierBytes() * (weight_per_data_byte - WITNESS_SCALE_FACTOR);
}
}
}
return mod_weight;
}

View File

@ -48,6 +48,8 @@ static constexpr unsigned int DEFAULT_INCREMENTAL_RELAY_FEE{1000};
static constexpr unsigned int DEFAULT_BYTES_PER_SIGOP{20}; static constexpr unsigned int DEFAULT_BYTES_PER_SIGOP{20};
/** Default for -bytespersigopstrict */ /** Default for -bytespersigopstrict */
static constexpr unsigned int DEFAULT_BYTES_PER_SIGOP_STRICT{20}; static constexpr unsigned int DEFAULT_BYTES_PER_SIGOP_STRICT{20};
/** Default for -datacarriercost (multiplied by WITNESS_SCALE_FACTOR) */
static constexpr unsigned int DEFAULT_WEIGHT_PER_DATA_BYTE{1};
/** Default for -permitbarepubkey */ /** Default for -permitbarepubkey */
static constexpr bool DEFAULT_PERMIT_BAREPUBKEY{true}; static constexpr bool DEFAULT_PERMIT_BAREPUBKEY{true};
/** Default for -permitbaremultisig */ /** Default for -permitbaremultisig */
@ -201,4 +203,6 @@ std::pair<CScript, unsigned int> GetScriptForTransactionInput(CScript prevScript
size_t DatacarrierBytes(const CTransaction& tx, const CCoinsViewCache& view); size_t DatacarrierBytes(const CTransaction& tx, const CCoinsViewCache& view);
int32_t CalculateExtraTxWeight(const CTransaction& tx, const CCoinsViewCache& view, const unsigned int weight_per_data_byte);
#endif // BITCOIN_POLICY_POLICY_H #endif // BITCOIN_POLICY_POLICY_H

View File

@ -9,3 +9,4 @@
unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP; unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP;
unsigned int nBytesPerSigOpStrict = DEFAULT_BYTES_PER_SIGOP_STRICT; unsigned int nBytesPerSigOpStrict = DEFAULT_BYTES_PER_SIGOP_STRICT;
unsigned int g_weight_per_data_byte = DEFAULT_WEIGHT_PER_DATA_BYTE;

View File

@ -8,5 +8,6 @@
extern unsigned int nBytesPerSigOp; extern unsigned int nBytesPerSigOp;
extern unsigned int nBytesPerSigOpStrict; extern unsigned int nBytesPerSigOpStrict;
extern unsigned int g_weight_per_data_byte;
#endif // BITCOIN_POLICY_SETTINGS_H #endif // BITCOIN_POLICY_SETTINGS_H

View File

@ -38,6 +38,7 @@ CTxMemPoolEntry ConsumeTxMemPoolEntry(FuzzedDataProvider& fuzzed_data_provider,
const double coin_age = fuzzed_data_provider.ConsumeFloatingPoint<double>(); const double coin_age = fuzzed_data_provider.ConsumeFloatingPoint<double>();
const unsigned int entry_height = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, std::numeric_limits<unsigned int>::max() - 1); const unsigned int entry_height = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, std::numeric_limits<unsigned int>::max() - 1);
const bool spends_coinbase = fuzzed_data_provider.ConsumeBool(); const bool spends_coinbase = fuzzed_data_provider.ConsumeBool();
const int32_t extra_weight = fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(0, GetTransactionWeight(tx) * 3);
const unsigned int sig_op_cost = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, MAX_BLOCK_SIGOPS_COST); const unsigned int sig_op_cost = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, MAX_BLOCK_SIGOPS_COST);
return CTxMemPoolEntry{MakeTransactionRef(tx), fee, time, entry_height, entry_sequence, /*entry_tx_inputs_coin_age=*/coin_age, tx.GetValueOut(), spends_coinbase, sig_op_cost, {}}; return CTxMemPoolEntry{MakeTransactionRef(tx), fee, time, entry_height, entry_sequence, /*entry_tx_inputs_coin_age=*/coin_age, tx.GetValueOut(), spends_coinbase, extra_weight, sig_op_cost, {}};
} }

View File

@ -566,7 +566,7 @@ std::vector<CTransactionRef> TestChain100Setup::PopulateMempool(FastRandomContex
/*time=*/0, /*entry_height=*/ height, /*entry_sequence=*/0, /*time=*/0, /*entry_height=*/ height, /*entry_sequence=*/0,
/*entry_tx_inputs_coin_age=*/coin_age, /*entry_tx_inputs_coin_age=*/coin_age,
in_chain_input_value, in_chain_input_value,
/*spends_coinbase=*/false, /*sigops_cost=*/4, lp)); /*spends_coinbase=*/false, /*extra_weight=*/0, /*sigops_cost=*/4, lp));
} }
--num_transactions; --num_transactions;
} }
@ -598,7 +598,7 @@ void TestChain100Setup::MockMempoolMinFee(const CFeeRate& target_feerate)
/*time=*/0, /*entry_height=*/1, /*entry_sequence=*/0, /*time=*/0, /*entry_height=*/1, /*entry_sequence=*/0,
/*entry_tx_inputs_coin_age=*/0.0, /*entry_tx_inputs_coin_age=*/0.0,
/*in_chain_input_value=*/0, /*in_chain_input_value=*/0,
/*spends_coinbase=*/true, /*sigops_cost=*/1, lp)); /*spends_coinbase=*/true, /*extra_weight=*/0, /*sigops_cost=*/1, lp));
m_node.mempool->TrimToSize(0); m_node.mempool->TrimToSize(0);
assert(m_node.mempool->GetMinFee() == target_feerate); assert(m_node.mempool->GetMinFee() == target_feerate);
} }

View File

@ -39,7 +39,7 @@ CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransactionRef& tx) const
{ {
constexpr double coin_age{0}; constexpr double coin_age{0};
const CAmount inChainValue = 0; const CAmount inChainValue = 0;
return CTxMemPoolEntry{tx, nFee, TicksSinceEpoch<std::chrono::seconds>(time), nHeight, m_sequence, /*entry_tx_inputs_coin_age=*/coin_age, inChainValue, spendsCoinbase, sigOpCost, lp}; return CTxMemPoolEntry{tx, nFee, TicksSinceEpoch<std::chrono::seconds>(time), nHeight, m_sequence, /*entry_tx_inputs_coin_age=*/coin_age, inChainValue, spendsCoinbase, /*extra_weight=*/0, sigOpCost, lp};
} }
std::optional<std::string> CheckPackageMempoolAcceptResult(const Package& txns, std::optional<std::string> CheckPackageMempoolAcceptResult(const Package& txns,

View File

@ -1048,10 +1048,11 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// Set entry_sequence to 0 when rejectmsg_zero_mempool_entry_seq is used; this allows txs from a block // Set entry_sequence to 0 when rejectmsg_zero_mempool_entry_seq is used; this allows txs from a block
// reorg to be marked earlier than any child txs that were already in the mempool. // reorg to be marked earlier than any child txs that were already in the mempool.
const uint64_t entry_sequence = args.m_ignore_rejects.count(rejectmsg_zero_mempool_entry_seq) ? 0 : m_pool.GetSequence(); const uint64_t entry_sequence = args.m_ignore_rejects.count(rejectmsg_zero_mempool_entry_seq) ? 0 : m_pool.GetSequence();
int32_t extra_weight = CalculateExtraTxWeight(*ptx, m_view, ::g_weight_per_data_byte);
entry.reset(new CTxMemPoolEntry(ptx, ws.m_base_fees, nAcceptTime, m_active_chainstate.m_chain.Height(), entry_sequence, entry.reset(new CTxMemPoolEntry(ptx, ws.m_base_fees, nAcceptTime, m_active_chainstate.m_chain.Height(), entry_sequence,
/*entry_tx_inputs_coin_age=*/coin_age, /*entry_tx_inputs_coin_age=*/coin_age,
inChainInputValue, inChainInputValue,
fSpendsCoinbase, nSigOpsCost, lock_points.value())); fSpendsCoinbase, extra_weight, nSigOpsCost, lock_points.value()));
ws.m_vsize = entry->GetTxSize(); ws.m_vsize = entry->GetTxSize();
entry->mapSPK = mapSPK; entry->mapSPK = mapSPK;

View File

@ -15,7 +15,7 @@ import re
FOLDER_GREP = 'src' FOLDER_GREP = 'src'
FOLDER_TEST = 'src/test/' FOLDER_TEST = 'src/test/'
REGEX_ARG = r'\b(?:GetArg|GetArgs|GetBoolArg|GetIntArg|GetPathArg|IsArgSet|get_net)\("(-[^"]+)"' REGEX_ARG = r'\b(?:GetArg|GetArgs|GetBoolArg|GetIntArg|GetFixedPointArg|GetPathArg|IsArgSet|get_net)\("(-[^"]+)"'
REGEX_DOC = r'AddArg\("(-[^"=]+?)(?:=|")' REGEX_DOC = r'AddArg\("(-[^"=]+?)(?:=|")'
CMD_ROOT_DIR = '$(git rev-parse --show-toplevel)/{}'.format(FOLDER_GREP) CMD_ROOT_DIR = '$(git rev-parse --show-toplevel)/{}'.format(FOLDER_GREP)
CMD_GREP_ARGS = r"git grep --perl-regexp '{}' -- {} ':(exclude){}'".format(REGEX_ARG, CMD_ROOT_DIR, FOLDER_TEST) CMD_GREP_ARGS = r"git grep --perl-regexp '{}' -- {} ':(exclude){}'".format(REGEX_ARG, CMD_ROOT_DIR, FOLDER_TEST)