diff --git a/src/miner.h b/src/miner.h --- a/src/miner.h +++ b/src/miner.h @@ -49,11 +49,19 @@ //!< Estimated package sigops (This is guaranteed to be >= real sigops) uint64_t packageSigOps; +private: + //!< These values are used the fee and size for the package with the + //!< greatest fee-rate seen thus far. + Amount maxPackageFee; + size_t maxPackageSize; + +public: CBlockTemplateEntry(CTransactionRef _tx, Amount _fees, Amount _modFees, uint64_t _size, int64_t _sigOps) : tx(_tx), txFee(_fees), txModFee(_modFees), txSize(_size), txSigOps(_sigOps), packageOrder(0), packageFee(_modFees), - packageSize(_size), packageSigOps(_sigOps) {} + packageSize(_size), packageSigOps(_sigOps), + maxPackageFee(Amount::zero()), maxPackageSize(0) {} /** * Calculate the feerate for this transaction. Use the minimum of the @@ -61,11 +69,26 @@ * end up "paying for" child transactions. */ CFeeRate FeeRate() const { - // In order to avoid numerical errors, we reorder to use multiplication - // instead of vision. - return int64_t(txSize) * packageFee < int64_t(packageSize) * txModFee - ? CFeeRate(packageFee, packageSize) - : CFeeRate(txModFee, txSize); + // If we are order 0, we know we can include the entire package for this + // fee rate. As there is no other package with a higher feerate, that we + // depend upon. + if (GetOrder() == 0) { + return CFeeRate(packageFee, packageSize); + } + + return CFeeRate(txModFee, txSize); + } + + size_t GetOrder() const { + // If we're the end of a maximum fee package thus far, set our maximum + // to our package fee, and reset our order. + if (int64_t(packageSize) * maxPackageFee <= + int64_t(maxPackageSize) * packageFee) { + // This package, is the highest feerate package. Nothing that this + // depends on could go in first. Return an order of '0' + return 0; + } + return packageOrder; } private: @@ -78,6 +101,42 @@ packageFee += parent.packageFee; packageSize += parent.packageSize; packageSigOps += parent.packageSigOps; + + // Propagate the maximum package fee, if it's bigger than anything we've + // already found. We know that AT LEAST this package would get in before + // us if we simply sort by package order. NOTE: Less than or equal to is + // important here, both sides are equal to zero upon first pass. + if (int64_t(parent.GetMaxPackageSize()) * maxPackageFee <= + int64_t(maxPackageSize) * parent.GetMaxPackageFee()) { + maxPackageFee = parent.GetMaxPackageFee(); + maxPackageSize = parent.GetMaxPackageSize(); + } + } + /** + * GetMaxPackageSize is a helper function exclusively for use in + * AccountForParent It is a shortcut for choosing either the maxPackageSize, + * or packageSize, depending on which feerate is greater. + */ + size_t GetMaxPackageSize() const { + if (int64_t(packageSize) * maxPackageFee <= + int64_t(maxPackageSize) * packageFee) { + return packageSize; + } + + return maxPackageSize; + } + /** + * GetMaxPackageFee is a helper function exclusively for use in + * AccountForParent It is a shortcut for choosing either the maxPackageSize, + * or packageFee, depending on which feerate is greater. + */ + Amount GetMaxPackageFee() const { + if (int64_t(packageSize) * maxPackageFee <= + int64_t(maxPackageSize) * packageFee) { + return packageFee; + } + + return maxPackageFee; } friend class BlockAssembler; diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -789,6 +789,9 @@ BOOST_CHECK_EQUAL(txEntry.packageFee, 2 * SATOSHI); BOOST_CHECK_EQUAL(txEntry.packageSize, 200); BOOST_CHECK_EQUAL(txEntry.packageSigOps, 10); + BOOST_CHECK_EQUAL(txEntry.GetOrder(), 0); + BOOST_CHECK(txEntry.FeeRate() == + CFeeRate(txEntry.packageFee, txEntry.packageSize)); CBlockTemplateEntry txChildEntry(txRef, 10 * SATOSHI, 11 * SATOSHI, 2000, 20); @@ -800,6 +803,26 @@ BOOST_CHECK_EQUAL(txChildEntry.packageFee, 13 * SATOSHI); BOOST_CHECK_EQUAL(txChildEntry.packageSize, 2200); BOOST_CHECK_EQUAL(txChildEntry.packageSigOps, 30); + BOOST_CHECK_EQUAL(txChildEntry.GetOrder(), 1); + BOOST_CHECK_EQUAL(txChildEntry.packageOrder, 1); + BOOST_CHECK(txChildEntry.FeeRate() == + CFeeRate(txChildEntry.txModFee, txChildEntry.txSize)); + + CBlockTemplateEntry txGrandChildEntry(txRef, 10 * SATOSHI, 1000 * SATOSHI, + 200, 20); + CBlockTemplateEntryTest::AccountForParent(txGrandChildEntry, txChildEntry); + BOOST_CHECK_EQUAL(txGrandChildEntry.txFee, 10 * SATOSHI); + BOOST_CHECK_EQUAL(txGrandChildEntry.txModFee, 1000 * SATOSHI); + BOOST_CHECK_EQUAL(txGrandChildEntry.txSize, 200); + BOOST_CHECK_EQUAL(txGrandChildEntry.txSigOps, 20); + BOOST_CHECK_EQUAL(txGrandChildEntry.packageFee, 1013 * SATOSHI); + BOOST_CHECK_EQUAL(txGrandChildEntry.packageSize, 2400); + BOOST_CHECK_EQUAL(txGrandChildEntry.packageSigOps, 50); + BOOST_CHECK_EQUAL(txGrandChildEntry.GetOrder(), 0); + BOOST_CHECK_EQUAL(txGrandChildEntry.packageOrder, 2); + BOOST_CHECK( + txGrandChildEntry.FeeRate() == + CFeeRate(txGrandChildEntry.packageFee, txGrandChildEntry.packageSize)); } BOOST_AUTO_TEST_SUITE_END()