Skip to content

Commit a4d7ac7

Browse files
authored
Merge pull request #1317 from delta1/ct-fees-discount
feat: discounted fees for confidential transactions
2 parents 7ff33d4 + 3ebc353 commit a4d7ac7

17 files changed

+653
-24
lines changed

src/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ BITCOIN_CORE_H = \
206206
noui.h \
207207
outputtype.h \
208208
pegins.h \
209+
policy/discount.h \
209210
policy/feerate.h \
210211
policy/fees.h \
211212
policy/packages.h \

src/chainparams.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,8 @@ class CMainParams : public CChainParams {
223223
anyonecanspend_aremine = false;
224224
enforce_pak = false;
225225
multi_data_permitted = false;
226+
accept_discount_ct = false;
227+
create_discount_ct = false;
226228
consensus.has_parent_chain = false;
227229
g_signed_blocks = false;
228230
g_con_elementsmode = false;
@@ -361,6 +363,8 @@ class CTestNetParams : public CChainParams {
361363
anyonecanspend_aremine = false;
362364
enforce_pak = false;
363365
multi_data_permitted = false;
366+
accept_discount_ct = false;
367+
create_discount_ct = false;
364368
consensus.has_parent_chain = false;
365369
g_signed_blocks = false;
366370
g_con_elementsmode = false;
@@ -517,6 +521,8 @@ class SigNetParams : public CChainParams {
517521
anyonecanspend_aremine = false;
518522
enforce_pak = false;
519523
multi_data_permitted = false;
524+
accept_discount_ct = false;
525+
create_discount_ct = false;
520526
consensus.has_parent_chain = false;
521527
g_signed_blocks = false; // lol
522528
g_con_elementsmode = false;
@@ -610,6 +616,8 @@ class CRegTestParams : public CChainParams {
610616
anyonecanspend_aremine = false;
611617
enforce_pak = false;
612618
multi_data_permitted = false;
619+
accept_discount_ct = false;
620+
create_discount_ct = false;
613621
consensus.has_parent_chain = false;
614622
g_signed_blocks = false;
615623
g_con_elementsmode = false;
@@ -887,6 +895,8 @@ class CCustomParams : public CRegTestParams {
887895
const CScript default_script(CScript() << OP_TRUE);
888896
consensus.fedpegScript = StrHexToScriptWithDefault(args.GetArg("-fedpegscript", ""), default_script);
889897
consensus.start_p2wsh_script = args.GetIntArg("-con_start_p2wsh_script", consensus.start_p2wsh_script);
898+
create_discount_ct = args.GetBoolArg("-creatediscountct", false);
899+
accept_discount_ct = args.GetBoolArg("-acceptdiscountct", create_discount_ct);
890900

891901
// Calculate pegged Bitcoin asset
892902
std::vector<unsigned char> commit = CommitToArguments(consensus, strNetworkID);
@@ -1023,7 +1033,7 @@ class CLiquidTestNetParams : public CCustomParams {
10231033
*/
10241034
class CLiquidV1Params : public CChainParams {
10251035
public:
1026-
CLiquidV1Params()
1036+
explicit CLiquidV1Params(const ArgsManager& args)
10271037
{
10281038

10291039
strNetworkID = "liquidv1";
@@ -1118,6 +1128,8 @@ class CLiquidV1Params : public CChainParams {
11181128
enforce_pak = true;
11191129

11201130
multi_data_permitted = true;
1131+
create_discount_ct = args.GetBoolArg("-creatediscountct", false);
1132+
accept_discount_ct = args.GetBoolArg("-acceptdiscountct", false);
11211133

11221134
parentGenesisBlockHash = uint256S("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
11231135
const bool parent_genesis_is_null = parentGenesisBlockHash == uint256();
@@ -1261,7 +1273,7 @@ class CLiquidV1Params : public CChainParams {
12611273
*/
12621274
class CLiquidV1TestParams : public CLiquidV1Params {
12631275
public:
1264-
explicit CLiquidV1TestParams(const ArgsManager& args)
1276+
explicit CLiquidV1TestParams(const ArgsManager& args) : CLiquidV1Params(args)
12651277
{
12661278
// Our goal here is to override ONLY the things from liquidv1 that make no sense for a test chain / which are pointless and burdensome to require people to override manually.
12671279

@@ -1466,6 +1478,8 @@ class CLiquidV1TestParams : public CLiquidV1Params {
14661478
enforce_pak = args.GetBoolArg("-enforce_pak", enforce_pak);
14671479

14681480
multi_data_permitted = args.GetBoolArg("-multi_data_permitted", multi_data_permitted);
1481+
create_discount_ct = args.GetBoolArg("-creatediscountct", create_discount_ct);
1482+
accept_discount_ct = args.GetBoolArg("-acceptdiscountct", accept_discount_ct || create_discount_ct);
14691483

14701484
if (args.IsArgSet("-parentgenesisblockhash")) {
14711485
parentGenesisBlockHash = uint256S(args.GetArg("-parentgenesisblockhash", ""));
@@ -1557,7 +1571,7 @@ std::unique_ptr<const CChainParams> CreateChainParams(const ArgsManager& args, c
15571571
} else if (chain == CBaseChainParams::REGTEST) {
15581572
return std::unique_ptr<CChainParams>(new CRegTestParams(args));
15591573
} else if (chain == CBaseChainParams::LIQUID1) {
1560-
return std::unique_ptr<CChainParams>(new CLiquidV1Params());
1574+
return std::unique_ptr<CChainParams>(new CLiquidV1Params(args));
15611575
} else if (chain == CBaseChainParams::LIQUID1TEST) {
15621576
return std::unique_ptr<CChainParams>(new CLiquidV1TestParams(args));
15631577
} else if (chain == CBaseChainParams::LIQUIDTESTNET) {

src/chainparams.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ class CChainParams
135135
const std::string& ParentBlech32HRP() const { return parent_blech32_hrp; }
136136
bool GetEnforcePak() const { return enforce_pak; }
137137
bool GetMultiDataPermitted() const { return multi_data_permitted; }
138+
bool GetAcceptDiscountCT() const { return accept_discount_ct; }
139+
bool GetCreateDiscountCT() const { return create_discount_ct; }
138140

139141
protected:
140142
CChainParams() {}
@@ -167,6 +169,8 @@ class CChainParams
167169
std::string parent_blech32_hrp;
168170
bool enforce_pak;
169171
bool multi_data_permitted;
172+
bool accept_discount_ct;
173+
bool create_discount_ct;
170174
};
171175

172176
/**

src/core_write.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <consensus/validation.h>
1010
#include <issuance.h>
1111
#include <key_io.h>
12+
#include <policy/discount.h> // ELEMENTS
1213
#include <script/descriptor.h>
1314
#include <script/script.h>
1415
#include <script/sign.h>
@@ -236,6 +237,10 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
236237
entry.pushKV("version", static_cast<int64_t>(static_cast<uint32_t>(tx.nVersion)));
237238
entry.pushKV("size", (int)::GetSerializeSize(tx, PROTOCOL_VERSION));
238239
entry.pushKV("vsize", (GetTransactionWeight(tx) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR);
240+
// ELEMENTS: add discountvsize
241+
if (Params().GetAcceptDiscountCT()) {
242+
entry.pushKV("discountvsize", GetDiscountVirtualTransactionSize(tx));
243+
}
239244
entry.pushKV("weight", GetTransactionWeight(tx));
240245
entry.pushKV("locktime", (int64_t)tx.nLockTime);
241246

src/init.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,8 @@ void SetupServerArgs(ArgsManager& argsman)
640640
argsman.AddArg("-initialreissuancetokens=<n>", "The amount of reissuance tokens created in the genesis block. (default: 0)", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
641641
argsman.AddArg("-ct_bits", strprintf("The default number of hiding bits in a rangeproof. Will be exceeded to cover amounts exceeding the maximum hiding value. (default: %d)", 52), ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
642642
argsman.AddArg("-ct_exponent", strprintf("The hiding exponent. (default: %s)", 0), ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
643+
argsman.AddArg("-acceptdiscountct", "Accept discounted fees for Confidential Transactions (default: true for liquidv1, false for other chains)", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
644+
argsman.AddArg("-creatediscountct", "Create Confidential Transactions with discounted fees (default: false). Setting this to true will also set 'acceptdiscountct' to true.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
643645

644646
#if defined(USE_SYSCALL_SANDBOX)
645647
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);

src/net_processing.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4964,7 +4964,9 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
49644964
auto txid = txinfo.tx->GetHash();
49654965
auto wtxid = txinfo.tx->GetWitnessHash();
49664966
// Peer told you to not send transactions at that feerate? Don't bother sending it.
4967-
if (txinfo.fee < filterrate.GetFee(txinfo.vsize)) {
4967+
// ELEMENTS: use the discounted vsize here so that discounted CTs are relayed.
4968+
// discountvsize only differs from vsize if accept_discount_ct is true.
4969+
if (txinfo.fee < filterrate.GetFee(txinfo.discountvsize)) {
49684970
continue;
49694971
}
49704972
if (pto->m_tx_relay->pfilter && !pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;

src/node/miner.cpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ int BlockAssembler::UpdatePackagesForAdded(const CTxMemPool::setEntries& already
320320
if (mit == mapModifiedTx.end()) {
321321
CTxMemPoolModifiedEntry modEntry(desc);
322322
modEntry.nSizeWithAncestors -= it->GetTxSize();
323+
modEntry.discountSizeWithAncestors -= it->GetDiscountTxSize();
323324
modEntry.nModFeesWithAncestors -= it->GetModifiedFee();
324325
modEntry.nSigOpCostWithAncestors -= it->GetSigOpCost();
325326
mapModifiedTx.insert(modEntry);
@@ -383,7 +384,8 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda
383384
// and modifying them for their already included ancestors
384385
UpdatePackagesForAdded(inBlock, mapModifiedTx);
385386

386-
CTxMemPool::indexed_transaction_set::index<ancestor_score>::type::iterator mi = m_mempool.mapTx.get<ancestor_score>().begin();
387+
// ELEMENTS: we use confidential_score instead of ancestor_score
388+
CTxMemPool::indexed_transaction_set::index<confidential_score>::type::iterator mi = m_mempool.mapTx.get<confidential_score>().begin();
387389
CTxMemPool::txiter iter;
388390

389391
// Limit the number of attempts to add transactions to the block when it is
@@ -392,9 +394,9 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda
392394
const int64_t MAX_CONSECUTIVE_FAILURES = 1000;
393395
int64_t nConsecutiveFailed = 0;
394396

395-
while (mi != m_mempool.mapTx.get<ancestor_score>().end() || !mapModifiedTx.empty()) {
397+
while (mi != m_mempool.mapTx.get<confidential_score>().end() || !mapModifiedTx.empty()) {
396398
// First try to find a new transaction in mapTx to evaluate.
397-
if (mi != m_mempool.mapTx.get<ancestor_score>().end() &&
399+
if (mi != m_mempool.mapTx.get<confidential_score>().end() &&
398400
SkipMapTxEntry(m_mempool.mapTx.project<0>(mi), mapModifiedTx, failedTx)) {
399401
++mi;
400402
continue;
@@ -404,16 +406,16 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda
404406
// the next entry from mapTx, or the best from mapModifiedTx?
405407
bool fUsingModified = false;
406408

407-
modtxscoreiter modit = mapModifiedTx.get<ancestor_score>().begin();
408-
if (mi == m_mempool.mapTx.get<ancestor_score>().end()) {
409+
modconftxscoreiter modit = mapModifiedTx.get<confidential_score>().begin();
410+
if (mi == m_mempool.mapTx.get<confidential_score>().end()) {
409411
// We're out of entries in mapTx; use the entry from mapModifiedTx
410412
iter = modit->iter;
411413
fUsingModified = true;
412414
} else {
413415
// Try to compare the mapTx entry to the mapModifiedTx entry
414416
iter = m_mempool.mapTx.project<0>(mi);
415-
if (modit != mapModifiedTx.get<ancestor_score>().end() &&
416-
CompareTxMemPoolEntryByAncestorFee()(*modit, CTxMemPoolModifiedEntry(iter))) {
417+
if (modit != mapModifiedTx.get<confidential_score>().end() &&
418+
CompareTxMemPoolEntryByConfidentialFee()(*modit, CTxMemPoolModifiedEntry(iter))) {
417419
// The best entry in mapModifiedTx has higher score
418420
// than the one from mapTx.
419421
// Switch which transaction (package) to consider
@@ -455,7 +457,7 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda
455457
// Since we always look at the best entry in mapModifiedTx,
456458
// we must erase failed entries so that we can consider the
457459
// next best entry on the next loop iteration
458-
mapModifiedTx.get<ancestor_score>().erase(modit);
460+
mapModifiedTx.get<confidential_score>().erase(modit);
459461
failedTx.insert(iter);
460462
}
461463

@@ -480,7 +482,7 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda
480482
// Test if all tx's are Final
481483
if (!TestPackageTransactions(ancestors)) {
482484
if (fUsingModified) {
483-
mapModifiedTx.get<ancestor_score>().erase(modit);
485+
mapModifiedTx.get<confidential_score>().erase(modit);
484486
failedTx.insert(iter);
485487
}
486488
continue;

src/node/miner.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,22 @@ struct CTxMemPoolModifiedEntry {
4141
{
4242
iter = entry;
4343
nSizeWithAncestors = entry->GetSizeWithAncestors();
44+
discountSizeWithAncestors = entry->GetDiscountSizeWithAncestors();
4445
nModFeesWithAncestors = entry->GetModFeesWithAncestors();
4546
nSigOpCostWithAncestors = entry->GetSigOpCostWithAncestors();
4647
}
4748

4849
int64_t GetModifiedFee() const { return iter->GetModifiedFee(); }
4950
uint64_t GetSizeWithAncestors() const { return nSizeWithAncestors; }
51+
uint64_t GetDiscountSizeWithAncestors() const { return discountSizeWithAncestors; }
5052
CAmount GetModFeesWithAncestors() const { return nModFeesWithAncestors; }
5153
size_t GetTxSize() const { return iter->GetTxSize(); }
54+
size_t GetDiscountTxSize() const { return iter->GetDiscountTxSize(); }
5255
const CTransaction& GetTx() const { return iter->GetTx(); }
5356

5457
CTxMemPool::txiter iter;
5558
uint64_t nSizeWithAncestors;
59+
uint64_t discountSizeWithAncestors;
5660
CAmount nModFeesWithAncestors;
5761
int64_t nSigOpCostWithAncestors;
5862
};
@@ -103,12 +107,19 @@ typedef boost::multi_index_container<
103107
boost::multi_index::tag<ancestor_score>,
104108
boost::multi_index::identity<CTxMemPoolModifiedEntry>,
105109
CompareTxMemPoolEntryByAncestorFee
110+
>,
111+
// ELEMENTS
112+
boost::multi_index::ordered_non_unique<
113+
boost::multi_index::tag<confidential_score>,
114+
boost::multi_index::identity<CTxMemPoolModifiedEntry>,
115+
CompareTxMemPoolEntryByConfidentialFee
106116
>
107117
>
108118
> indexed_modified_transaction_set;
109119

110120
typedef indexed_modified_transaction_set::nth_index<0>::type::iterator modtxiter;
111121
typedef indexed_modified_transaction_set::index<ancestor_score>::type::iterator modtxscoreiter;
122+
typedef indexed_modified_transaction_set::index<confidential_score>::type::iterator modconftxscoreiter; // ELEMENTS
112123

113124
struct update_for_parent_inclusion
114125
{

src/policy/discount.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (c) 2009-2010 Satoshi Nakamoto
2+
// Copyright (c) 2009-2021 The Bitcoin Core developers
3+
// Distributed under the MIT software license, see the accompanying
4+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
6+
#ifndef BITCOIN_POLICY_DISCOUNT_H
7+
#define BITCOIN_POLICY_DISCOUNT_H
8+
9+
#include <consensus/consensus.h>
10+
#include <cstdint>
11+
#include <primitives/transaction.h>
12+
#include <version.h>
13+
14+
/**
15+
* Calculate a smaller virtual size for discounted Confidential Transactions.
16+
*/
17+
static inline int64_t GetDiscountVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost = 0, unsigned int bytes_per_sig_op = 0)
18+
{
19+
int64_t size_bytes = ::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(tx, PROTOCOL_VERSION);
20+
int64_t sigop_bytes = nSigOpCost * bytes_per_sig_op;
21+
22+
int64_t weight = std::max(size_bytes, sigop_bytes);
23+
24+
// for each confidential output
25+
for (size_t i = 0; i < tx.vout.size(); ++i) {
26+
const CTxOut& output = tx.vout[i];
27+
if (i < tx.witness.vtxoutwit.size()) {
28+
// subtract the weight of the output witness, except the 2 bytes used to serialize the empty proofs
29+
size_t witness_size = ::GetSerializeSize(tx.witness.vtxoutwit[i], PROTOCOL_VERSION);
30+
assert(witness_size >= 2);
31+
weight -= (witness_size - 2);
32+
}
33+
if (output.nValue.IsCommitment()) {
34+
// subtract the weight difference of amount commitment (33) vs explicit amount (9)
35+
weight -= (33 - 9);
36+
}
37+
if (output.nNonce.IsCommitment()) {
38+
// subtract the weight difference of nonce commitment (33) vs no nonce (1)
39+
weight -= 32;
40+
}
41+
}
42+
assert(weight > 0);
43+
44+
size_t discountvsize = (weight + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
45+
46+
assert(discountvsize > 0);
47+
return discountvsize;
48+
}
49+
50+
#endif // BITCOIN_POLICY_DISCOUNT_H

src/policy/policy.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#define BITCOIN_POLICY_POLICY_H
88

99
#include <consensus/consensus.h>
10+
#include <policy/discount.h>
1011
#include <policy/feerate.h>
1112
#include <script/interpreter.h>
1213
#include <script/standard.h>

0 commit comments

Comments
 (0)