Skip to content

discount: fix weight calculation #1366

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 25, 2024
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
3 changes: 2 additions & 1 deletion src/core_write.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,11 +237,12 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
entry.pushKV("version", static_cast<int64_t>(static_cast<uint32_t>(tx.nVersion)));
entry.pushKV("size", (int)::GetSerializeSize(tx, PROTOCOL_VERSION));
entry.pushKV("vsize", (GetTransactionWeight(tx) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR);
entry.pushKV("weight", GetTransactionWeight(tx));
// ELEMENTS: add discountvsize
if (Params().GetAcceptDiscountCT()) {
entry.pushKV("discountvsize", GetDiscountVirtualTransactionSize(tx));
entry.pushKV("discountweight", GetDiscountTransactionWeight(tx));
}
entry.pushKV("weight", GetTransactionWeight(tx));
entry.pushKV("locktime", (int64_t)tx.nLockTime);

UniValue vin{UniValue::VARR};
Expand Down
20 changes: 14 additions & 6 deletions src/policy/discount.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,15 @@
#include <version.h>

/**
* Calculate a smaller virtual size for discounted Confidential Transactions.
* Calculate a smaller weight for discounted Confidential Transactions.
*/
static inline int64_t GetDiscountVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost = 0, unsigned int bytes_per_sig_op = 0)
static inline int64_t GetDiscountTransactionWeight(const CTransaction& tx, int64_t nSigOpCost = 0, unsigned int bytes_per_sig_op = 0)
{
int64_t size_bytes = ::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(tx, PROTOCOL_VERSION);
int64_t sigop_bytes = nSigOpCost * bytes_per_sig_op;

int64_t weight = std::max(size_bytes, sigop_bytes);

// for each confidential output
for (size_t i = 0; i < tx.vout.size(); ++i) {
const CTxOut& output = tx.vout[i];
if (i < tx.witness.vtxoutwit.size()) {
Expand All @@ -32,16 +31,25 @@ static inline int64_t GetDiscountVirtualTransactionSize(const CTransaction& tx,
}
if (output.nValue.IsCommitment()) {
// subtract the weight difference of amount commitment (33) vs explicit amount (9)
weight -= (33 - 9);
// weighted as part of the base transaction
weight -= (33 - 9) * WITNESS_SCALE_FACTOR;
}
if (output.nNonce.IsCommitment()) {
// subtract the weight difference of nonce commitment (33) vs no nonce (1)
weight -= 32;
// weighted as part of the base transaction
weight -= 32 * WITNESS_SCALE_FACTOR;
}
}
assert(weight > 0);
return weight;
}

size_t discountvsize = (weight + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
/**
* Calculate a smaller virtual size for discounted Confidential Transactions.
*/
static inline int64_t GetDiscountVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost = 0, unsigned int bytes_per_sig_op = 0)
{
size_t discountvsize = (GetDiscountTransactionWeight(tx, nSigOpCost, bytes_per_sig_op) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;

assert(discountvsize > 0);
return discountvsize;
Expand Down
32 changes: 22 additions & 10 deletions test/functional/feature_discount_ct.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,10 @@ def run_test(self):
assert_equal(len(vout), 3)
assert_equal(tx['fee']['bitcoin'], Decimal('-0.00000326'))
assert_equal(decoded['vsize'], 326)
assert_equal(decoded['weight'], 1302)
self.generate(node0, 1)
tx = node1.getrawtransaction(txid, True)
assert_equal(tx['discountweight'], 1302)
assert_equal(tx['discountvsize'], 326)

self.log.info("Send confidential tx to node 0")
Expand All @@ -95,9 +97,11 @@ def run_test(self):
assert_equal(len(vout), 3)
assert_equal(tx['fee']['bitcoin'], Decimal('-0.00002575'))
assert_equal(decoded['vsize'], 2575)
assert_equal(decoded['weight'], 10300)
self.generate(node0, 1)
tx = node1.getrawtransaction(txid, True)
assert_equal(tx['discountvsize'], 410) # node1 has discountvsize
assert_equal(tx['discountweight'], 1302)
assert_equal(tx['discountvsize'], 326) # node1 has discountvsize

self.log.info("Send explicit tx to node 1")
addr = node1.getnewaddress()
Expand All @@ -111,8 +115,10 @@ def run_test(self):
assert_equal(len(vout), 3)
assert_equal(tx['fee']['bitcoin'], Decimal('-0.00000326'))
assert_equal(decoded['vsize'], 326)
assert_equal(decoded['weight'], 1302)
self.generate(node0, 1)
tx = node1.getrawtransaction(txid, True)
assert_equal(tx['discountweight'], 1302)
assert_equal(tx['discountvsize'], 326)

self.log.info("Send confidential (undiscounted) tx to node 1")
Expand All @@ -127,9 +133,11 @@ def run_test(self):
assert_equal(len(vout), 3)
assert_equal(tx['fee']['bitcoin'], Decimal('-0.00002575'))
assert_equal(decoded['vsize'], 2575)
assert_equal(decoded['weight'], 10300)
self.generate(node0, 1)
tx = node1.getrawtransaction(txid, True)
assert_equal(tx['discountvsize'], 410) # node1 has discountvsize
assert_equal(tx['discountweight'], 1302)
assert_equal(tx['discountvsize'], 326) # node1 has discountvsize

self.log.info("Send confidential (discounted) tx to node 1")
bitcoin = 'b2e15d0d7a0c94e4e2ce0fe6e8691b9e451377f6e46e8045a86f7c4b5d4f0f23'
Expand All @@ -148,11 +156,13 @@ def run_test(self):
assert_equal(len(vin), 2)
assert_equal(len(vout), 3)
if 'bitcoin' in decoded['fee']:
assert_equal(decoded['fee']['bitcoin'], Decimal('-0.00000410'))
assert_equal(decoded['fee']['bitcoin'], Decimal('-0.00000326'))
else:
assert_equal(decoded['fee'][bitcoin], Decimal('0.00000410'))
assert_equal(decoded['fee'][bitcoin], Decimal('0.00000326'))
assert_equal(decoded['vsize'], 2575)
assert_equal(decoded['discountvsize'], 410)
assert_equal(decoded['weight'], 10300)
assert_equal(decoded['discountweight'], 1302)
assert_equal(decoded['discountvsize'], 326)

# node0 only has vsize
tx = node0.getrawtransaction(txid, True)
Expand All @@ -176,11 +186,13 @@ def run_test(self):
assert_equal(len(vin), 2)
assert_equal(len(vout), 3)
if 'bitcoin' in decoded['fee']:
assert_equal(decoded['fee']['bitcoin'], Decimal('-0.00000041'))
assert_equal(decoded['fee']['bitcoin'], Decimal('-0.00000033'))
else:
assert_equal(decoded['fee'][bitcoin], Decimal('0.00000041'))
assert_equal(decoded['fee'][bitcoin], Decimal('0.00000033'))
assert_equal(decoded['vsize'], 2575)
assert_equal(decoded['discountvsize'], 410)
assert_equal(decoded['weight'], 10300)
assert_equal(decoded['discountweight'], 1302)
assert_equal(decoded['discountvsize'], 326)
# node0 only has vsize
tx = node0.getrawtransaction(txid, True)
assert_equal(tx['vsize'], 2575)
Expand All @@ -207,7 +219,7 @@ def run_test(self):
assert_equal(test[0]["allowed"], True)
txid = node1.sendrawtransaction(signed['hex'])
tx = node1.gettransaction(txid, True, True)
assert_equal(tx['decoded']['discountvsize'], 341)
assert_equal(tx['decoded']['discountvsize'], 257)

for i in range(24):
self.log.info(f"Add package descendant {i+1}")
Expand All @@ -231,7 +243,7 @@ def run_test(self):
assert_equal(test[0]["allowed"], True)
txid = node1.sendrawtransaction(hex)
tx = node1.gettransaction(txid, True, True)
assert_equal(tx['decoded']['discountvsize'], 341)
assert_equal(tx['decoded']['discountvsize'], 257)
assert_equal(len(node1.getrawmempool()), i + 2)

assert_equal(len(node1.getrawmempool()), 25)
Expand Down
10 changes: 5 additions & 5 deletions test/functional/feature_discount_ct_ordering.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def run_test(self):
assert_equal(decoded['vsize'], 2575)
self.sync_mempools([node0, node1])
tx = node1.getrawtransaction(txid, True)
assert_equal(tx['discountvsize'], 410)
assert_equal(tx['discountvsize'], 326)

feerate = 1.0
self.log.info(f"Send confidential (discounted) tx to node 1 at {feerate} sat/vb")
Expand All @@ -131,11 +131,11 @@ def run_test(self):
assert_equal(len(vin), 2)
assert_equal(len(vout), 3)
if 'bitcoin' in decoded['fee']:
assert_equal(decoded['fee']['bitcoin'], Decimal('-0.00000410'))
assert_equal(decoded['fee']['bitcoin'], Decimal('-0.00000326'))
else:
assert_equal(decoded['fee'][bitcoin], Decimal('0.00000410'))
assert_equal(decoded['fee'][bitcoin], Decimal('0.00000326'))
assert_equal(decoded['vsize'], 2575)
assert_equal(decoded['discountvsize'], 410)
assert_equal(decoded['discountvsize'], 326)

feerate = 2.0
self.log.info(f"Send confidential (discounted) tx to node 1 at {feerate} sat/vb")
Expand All @@ -145,7 +145,7 @@ def run_test(self):
self.sync_mempools([node1, node2])
tx = node1.gettransaction(txid, True, True)
decoded = tx['decoded']
assert_equal(decoded['fee'][bitcoin], Decimal('0.00000820'))
assert_equal(decoded['fee'][bitcoin], Decimal('0.00000652'))

# check that txs in the block template are in decreasing feerate according to their discount size
self.log.info("Check tx ordering in block template")
Expand Down