From d0f714638ac565fbfeccd9a7940d827c19ce0993 Mon Sep 17 00:00:00 2001 From: Byron Hambly Date: Fri, 20 Sep 2024 14:39:13 +0200 Subject: [PATCH 1/2] feat: add discountweight to TxToUniv --- src/core_write.cpp | 3 ++- src/policy/discount.h | 13 ++++++++++--- test/functional/feature_discount_ct.py | 12 ++++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/core_write.cpp b/src/core_write.cpp index d9fc2a7a59..d61b89900f 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -237,11 +237,12 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, entry.pushKV("version", static_cast(static_cast(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}; diff --git a/src/policy/discount.h b/src/policy/discount.h index e8c3ece6e0..1cfec410af 100644 --- a/src/policy/discount.h +++ b/src/policy/discount.h @@ -12,9 +12,9 @@ #include /** - * 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; @@ -40,8 +40,15 @@ static inline int64_t GetDiscountVirtualTransactionSize(const CTransaction& tx, } } 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; diff --git a/test/functional/feature_discount_ct.py b/test/functional/feature_discount_ct.py index b0b43361e6..85a341df5b 100755 --- a/test/functional/feature_discount_ct.py +++ b/test/functional/feature_discount_ct.py @@ -79,8 +79,10 @@ class CTTest(BitcoinTestFramework): 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") @@ -95,8 +97,10 @@ class CTTest(BitcoinTestFramework): 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['discountweight'], 1638) assert_equal(tx['discountvsize'], 410) # node1 has discountvsize self.log.info("Send explicit tx to node 1") @@ -111,8 +115,10 @@ class CTTest(BitcoinTestFramework): 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") @@ -127,8 +133,10 @@ class CTTest(BitcoinTestFramework): 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['discountweight'], 1638) assert_equal(tx['discountvsize'], 410) # node1 has discountvsize self.log.info("Send confidential (discounted) tx to node 1") @@ -152,6 +160,8 @@ class CTTest(BitcoinTestFramework): else: assert_equal(decoded['fee'][bitcoin], Decimal('0.00000410')) assert_equal(decoded['vsize'], 2575) + assert_equal(decoded['weight'], 10300) + assert_equal(decoded['discountweight'], 1638) assert_equal(decoded['discountvsize'], 410) # node0 only has vsize @@ -180,7 +190,9 @@ class CTTest(BitcoinTestFramework): else: assert_equal(decoded['fee'][bitcoin], Decimal('0.00000041')) assert_equal(decoded['vsize'], 2575) + assert_equal(decoded['weight'], 10300) assert_equal(decoded['discountvsize'], 410) + assert_equal(decoded['discountweight'], 1638) # node0 only has vsize tx = node0.getrawtransaction(txid, True) assert_equal(tx['vsize'], 2575) From 09c5068ab19390fc6553172c645b72ee3d6fecd8 Mon Sep 17 00:00:00 2001 From: Byron Hambly Date: Wed, 25 Sep 2024 15:18:38 +0200 Subject: [PATCH 2/2] discount: fix weight calculation Thanks to Jon Griffiths for picking up that the amount and nonce discount should be multiplied by the witness scaling factor as they form part of the base transaction. --- src/policy/discount.h | 7 +++-- test/functional/feature_discount_ct.py | 28 +++++++++---------- .../feature_discount_ct_ordering.py | 10 +++---- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/policy/discount.h b/src/policy/discount.h index 1cfec410af..9f087f62c9 100644 --- a/src/policy/discount.h +++ b/src/policy/discount.h @@ -21,7 +21,6 @@ static inline int64_t GetDiscountTransactionWeight(const CTransaction& tx, int64 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()) { @@ -32,11 +31,13 @@ static inline int64_t GetDiscountTransactionWeight(const CTransaction& tx, int64 } 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); diff --git a/test/functional/feature_discount_ct.py b/test/functional/feature_discount_ct.py index 85a341df5b..84e2c096b1 100755 --- a/test/functional/feature_discount_ct.py +++ b/test/functional/feature_discount_ct.py @@ -100,8 +100,8 @@ class CTTest(BitcoinTestFramework): assert_equal(decoded['weight'], 10300) self.generate(node0, 1) tx = node1.getrawtransaction(txid, True) - assert_equal(tx['discountweight'], 1638) - 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() @@ -136,8 +136,8 @@ class CTTest(BitcoinTestFramework): assert_equal(decoded['weight'], 10300) self.generate(node0, 1) tx = node1.getrawtransaction(txid, True) - assert_equal(tx['discountweight'], 1638) - 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' @@ -156,13 +156,13 @@ class CTTest(BitcoinTestFramework): 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['weight'], 10300) - assert_equal(decoded['discountweight'], 1638) - assert_equal(decoded['discountvsize'], 410) + assert_equal(decoded['discountweight'], 1302) + assert_equal(decoded['discountvsize'], 326) # node0 only has vsize tx = node0.getrawtransaction(txid, True) @@ -186,13 +186,13 @@ class CTTest(BitcoinTestFramework): 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['weight'], 10300) - assert_equal(decoded['discountvsize'], 410) - assert_equal(decoded['discountweight'], 1638) + assert_equal(decoded['discountweight'], 1302) + assert_equal(decoded['discountvsize'], 326) # node0 only has vsize tx = node0.getrawtransaction(txid, True) assert_equal(tx['vsize'], 2575) @@ -219,7 +219,7 @@ class CTTest(BitcoinTestFramework): 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}") @@ -243,7 +243,7 @@ class CTTest(BitcoinTestFramework): 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) diff --git a/test/functional/feature_discount_ct_ordering.py b/test/functional/feature_discount_ct_ordering.py index 7cca1ee880..aed8a1f0db 100755 --- a/test/functional/feature_discount_ct_ordering.py +++ b/test/functional/feature_discount_ct_ordering.py @@ -113,7 +113,7 @@ class CTTest(BitcoinTestFramework): 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") @@ -131,11 +131,11 @@ class CTTest(BitcoinTestFramework): 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") @@ -145,7 +145,7 @@ class CTTest(BitcoinTestFramework): 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")