rpc: Replace median fee rate with feerate percentiles in getblockstats #13918

pull marcinja wants to merge 1 commits into bitcoin:master from marcinja:change-feerates-rpc changing 5 files +162 −12
  1. marcinja commented at 9:54 PM on August 8, 2018: contributor

    Currently, the medianfeerate statistic is calculated from the feerate of the middle transaction of a list of transactions sorted by feerate.

    This PR instead uses the value of the 50th percentile weight unit in the block, and also calculates the feerate at the 10th, 25th, 75th, and 90th percentiles. This more accurately corresponds with what is generally meant by median feerate.

  2. jnewbery commented at 9:58 PM on August 8, 2018: member

    Concept ACK. The feerate is a measure on weight unit, not discrete transaction, so the median feerate should me the middle weight unit of the block.

    Conceptually, this gives a better representation of the fees paid in the block, since the measure is not skewed by small or large txs.

    Will review tomorrow.

  3. MarcoFalke commented at 10:04 PM on August 8, 2018: member

    utACK 2d4bf95973bb5224238762ce94e3ecb835b77280

  4. MarcoFalke added this to the milestone 0.17.0 on Aug 8, 2018
  5. MarcoFalke commented at 10:08 PM on August 8, 2018: member

    Imo this is a bugfix for 0.17, but not a blocker for rc1.

  6. fanquake added the label RPC/REST/ZMQ on Aug 8, 2018
  7. in src/rpc/blockchain.cpp:1663 in 2d4bf95973 outdated
    1658 | +    };
    1659 | +    int64_t next_percentile_index = 0;
    1660 | +    int64_t cumulative_weight = 0;
    1661 | +    for (const auto& element: scores) {
    1662 | +        cumulative_weight += element.second;
    1663 | +        while (cumulative_weight >= weights[next_percentile_index]) {
    


    ajtowns commented at 1:34 AM on August 9, 2018:

    This should be while (next_percentile_index < NUM_GETBLOCKSTATS_PERCENTILS && cumulative_weight >= weights[next_percentile_index]) {, no?


    marcinja commented at 3:58 AM on August 9, 2018:

    Yes, thanks for catching that.

  8. fanquake deleted a comment on Aug 9, 2018
  9. marcinja force-pushed on Aug 9, 2018
  10. in src/rpc/blockchain.cpp:1713 in 1fd565be86 outdated
    1709 | @@ -1679,7 +1710,13 @@ static UniValue getblockstats(const JSONRPCRequest& request)
    1710 |              "  \"maxfeerate\": xxxxx,      (numeric) Maximum feerate (in satoshis per virtual byte)\n"
    1711 |              "  \"maxtxsize\": xxxxx,       (numeric) Maximum transaction size\n"
    1712 |              "  \"medianfee\": xxxxx,       (numeric) Truncated median fee in the block\n"
    1713 | -            "  \"medianfeerate\": xxxxx,   (numeric) Truncated median feerate (in satoshis per virtual byte)\n"
    1714 | +            "  \"feerate_percentiles\": [   (array of numeric) Feerates at the 10th, 25th, 50th, 75th, and 90th percentile weight unit (in satoshis per virtual byte)\n"
    


    kallewoof commented at 4:24 AM on August 9, 2018:

    Nit: indentation is off for (array of numeric) [...] -- see above line.


    jnewbery commented at 2:10 PM on August 9, 2018:

    nit: the other result keys are dictionary sorted in the help text. I suggest you follow the same convention here.

  11. kallewoof commented at 4:26 AM on August 9, 2018: member

    utACK 1fd565be86e3727e8dabcb961f9a24c18bddd2a8

  12. in src/rpc/blockchain.cpp:1649 in 1fd565be86 outdated
    1644 | +
    1645 | +template<typename T>
    1646 | +static void CalculatePercentilesByWeight(T result[NUM_GETBLOCKSTATS_PERCENTILES], std::vector<std::pair<T, int64_t>>& scores, int64_t total_weight)
    1647 | +{
    1648 | +    const size_t size = scores.size();
    1649 | +    if (size == 0) {
    


    domob1812 commented at 9:53 AM on August 9, 2018:

    You don't use size anywhere else below - so you could just do:

    if (scores.empty()) {
        return;
    }

    jnewbery commented at 2:17 PM on August 9, 2018:

    :+1:

  13. in src/rpc/blockchain.cpp:1661 in 1fd565be86 outdated
    1656 | +    const double weights[NUM_GETBLOCKSTATS_PERCENTILES] = {
    1657 | +        total_weight / 10.0, total_weight / 4.0, total_weight / 2.0, (total_weight * 3.0) / 4.0, (total_weight * 9.0) / 10.0
    1658 | +    };
    1659 | +    int64_t next_percentile_index = 0;
    1660 | +    int64_t cumulative_weight = 0;
    1661 | +    for (const auto& element: scores) {
    


    domob1812 commented at 9:53 AM on August 9, 2018:

    Nit: I believe there should be spaces on both sides of :.

  14. in src/rpc/blockchain.cpp:1660 in 1fd565be86 outdated
    1658 | +    };
    1659 | +    int64_t next_percentile_index = 0;
    1660 | +    int64_t cumulative_weight = 0;
    1661 | +    for (const auto& element: scores) {
    1662 | +        cumulative_weight += element.second;
    1663 | +        while (next_percentile_index < NUM_GETBLOCKSTATS_PERCENTILES && cumulative_weight >= weights[next_percentile_index]) {
    


    conscott commented at 11:59 AM on August 9, 2018:

    This logic seems correct, but would be nice to have some test cases to ensure expected behavior for cases like:

    1. Few number of txs (expect same feerate for multiple percentiles)
    2. Tx whose weight spans more than one weight percentile group

    jnewbery commented at 2:19 PM on August 9, 2018:

    Agree that getblockstats could do with some more thorough testing, but this PR doesn't make things worse, so I'd be inclined to say leave it for a future PR.

  15. conscott commented at 11:59 AM on August 9, 2018: contributor

    utACK 1fd565be86e3727e8dabcb961f9a24c18bddd2a8

  16. in src/rpc/blockchain.cpp:1643 in 1fd565be86 outdated
    1639 | @@ -1640,6 +1640,37 @@ static T CalculateTruncatedMedian(std::vector<T>& scores)
    1640 |      }
    1641 |  }
    1642 |  
    1643 | +static constexpr int NUM_GETBLOCKSTATS_PERCENTILES = 5;
    


    jnewbery commented at 2:03 PM on August 9, 2018:

    nit: I think it's better to place constexprs at the top of the file (especially since this is used in multiple functions in this file)

  17. in src/rpc/blockchain.cpp:1645 in 1fd565be86 outdated
    1639 | @@ -1640,6 +1640,37 @@ static T CalculateTruncatedMedian(std::vector<T>& scores)
    1640 |      }
    1641 |  }
    1642 |  
    1643 | +static constexpr int NUM_GETBLOCKSTATS_PERCENTILES = 5;
    1644 | +
    1645 | +template<typename T>
    


    jnewbery commented at 2:04 PM on August 9, 2018:

    nit: using a template here is over-engineering this. Even if you plan to re-use this a measure other than fee-rate, it would make more sense to define this just for fee-rate now and then change to a template in the next PR.

  18. in src/rpc/blockchain.cpp:1667 in 1fd565be86 outdated
    1664 | +            result[next_percentile_index] = element.first;
    1665 | +            ++next_percentile_index;
    1666 | +        }
    1667 | +    }
    1668 | +
    1669 | +    for (int64_t i = next_percentile_index; i < NUM_GETBLOCKSTATS_PERCENTILES; i++) {
    


    jnewbery commented at 2:09 PM on August 9, 2018:

    nit: perhaps a comment here to say that you're filling in the remaining percentile results to the same as the last filled percentile result.

  19. in src/rpc/blockchain.cpp:1911 in 1fd565be86 outdated
    1906 | @@ -1867,7 +1907,13 @@ static UniValue getblockstats(const JSONRPCRequest& request)
    1907 |      ret_all.pushKV("maxfeerate", maxfeerate);
    1908 |      ret_all.pushKV("maxtxsize", maxtxsize);
    1909 |      ret_all.pushKV("medianfee", CalculateTruncatedMedian(fee_array));
    1910 | -    ret_all.pushKV("medianfeerate", CalculateTruncatedMedian(feerate_array));
    1911 | +
    1912 | +    UniValue feerates_res(UniValue::VARR);
    


    jnewbery commented at 2:14 PM on August 9, 2018:

    nit: I'd push this code up to immediately below the CalculatePercentilesByWeight() call so all the ret_all.pushKV() calls are on contiguous (alphabetized) lines.

  20. in test/functional/data/rpc_getblockstats.json:189 in 1fd565be86 outdated
     185 | @@ -174,13 +186,19 @@
     186 |        "avgfeerate": 109,
     187 |        "avgtxsize": 228,
     188 |        "blockhash": "22d9b8b9c2a37c81515f3fc84f7241f6c07dbcea85ef16b00bcc33ae400a030f",
     189 | +	"feerate_percentiles": [
    


    jnewbery commented at 2:14 PM on August 9, 2018:

    nit: replace tab with spaces please :)

  21. jnewbery commented at 2:18 PM on August 9, 2018: member

    Code looks correct so utACK 1fd565be86e3727e8dabcb961f9a24c18bddd2a8

    I've also got a bunch of nits for you :)

  22. marcinja force-pushed on Aug 9, 2018
  23. marcinja force-pushed on Aug 9, 2018
  24. marcinja force-pushed on Aug 9, 2018
  25. marcinja force-pushed on Aug 9, 2018
  26. marcinja commented at 4:19 PM on August 9, 2018: contributor

    Thanks for the reviews. I fixed all the nits and added some unit tests for CalculatePercentilesByWeight

    I'm not sure that I put the tests in the best location, and also this now fails on Travis because of linting

    ✗ bitcoin (change-feerates-rpc) ./test/lint/lint-includes.sh 
    The following files #include .cpp files:
    src/test/rpc_tests.cpp:#include <rpc/blockchain.cpp>
    
  27. in src/test/rpc_tests.cpp:19 in b4718d26ab outdated
      15 | @@ -16,6 +16,8 @@
      16 |  
      17 |  #include <univalue.h>
      18 |  
      19 | +#include <rpc/blockchain.cpp>
    


    MarcoFalke commented at 4:22 PM on August 9, 2018:

    Can't you just include the .h file?


    MarcoFalke commented at 4:22 PM on August 9, 2018:

    If there are static methods that you need, remove the static in the cpp file and add the declaration here


    marcinja commented at 5:25 PM on August 9, 2018:

    Yup, that works thanks. I had to move some of the other includes but I think that should be okay.

  28. in src/rpc/blockchain.cpp:1887 in b4718d26ab outdated
    1884 | @@ -1848,26 +1885,34 @@ static UniValue getblockstats(const JSONRPCRequest& request)
    1885 |  
    1886 |              // New feerate uses satoshis per virtual byte instead of per serialized byte
    1887 |              CAmount feerate = weight ? (txfee * WITNESS_SCALE_FACTOR) / weight : 0;
    1888 | -            if (do_medianfeerate) {
    1889 | -                feerate_array.push_back(feerate);
    1890 | +            if (do_feerate_percentiles) {
    1891 | +                feerate_array.emplace_back(std::make_pair(feerate, weight));
    


    domob1812 commented at 5:01 PM on August 9, 2018:

    With emplace_back, I think you should be able to get rid of make_pair - std::pair has a constructor with first and second, so that should be called then.


    marcinja commented at 6:41 PM on August 9, 2018:

    So use std::pair<CAmount, int64_t>(feerate, weight) instead? Is there a reason the constructor is preferred?


    MarcoFalke commented at 6:57 PM on August 9, 2018:

    Unless there is compelling reason to change the code, you should keep the it as is for now.


    domob1812 commented at 6:09 AM on August 10, 2018:

    No, I meant to just use freerate_array.emplace_back(feerate, weight). I haven't tested it, but according to my understanding it should work - and just be simpler in the code. Whether that is a compelling reason or not, I don't know.


    kallewoof commented at 6:32 AM on August 13, 2018:

    Agree with @domob1812 that

    feerate_array.emplace_back(feerate, weight);
    

    looks cleaner (and yes, it works).

  29. domob1812 commented at 5:02 PM on August 9, 2018: contributor

    utACK b4718d26ab785229ecfb7d89b593cb050d883fc3

  30. marcinja force-pushed on Aug 9, 2018
  31. DrahtBot commented at 6:29 PM on August 9, 2018: member

    <!--e57a25ab6845829454e8d69fc972939a-->Note to reviewers: This pull request conflicts with the following ones:

    • #11342 (Sanity assert GetAncestor() != nullptr where appropriate by danra)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

  32. achow101 commented at 11:50 PM on August 10, 2018: member

    utACK 64101856b4e9a110770e389d2a1cad6a5802321c

  33. sipa commented at 11:59 PM on August 10, 2018: member

    This breaks the RPC interface. Do we need to go through the rpc deprecation process, or should we should maintain a legacy medianfeerate field?

  34. achow101 commented at 12:25 AM on August 11, 2018: member

    @sipa getblockstats is not in any release, so I think this is actually fine to have a breaking API change.

  35. sipa commented at 12:39 AM on August 11, 2018: member

    Ah, I didn't notice this was considered to be a bugfix.

  36. Replace median fee rate with feerate percentiles
    Removes medianfeerate result from getblockstats.
    Adds feerate_percentiles which give the feerate of the 10th, 25th, 50th,
    75th, and 90th percentile weight unit in the block.
    4b7091a842
  37. in src/rpc/blockchain.cpp:35 in 64101856b4 outdated
      30 | @@ -32,7 +31,6 @@
      31 |  #include <warnings.h>
      32 |  
      33 |  #include <assert.h>
      34 | -#include <stdint.h>
    


    Empact commented at 12:48 AM on August 11, 2018:

    nit: you should leave these includes

    Every .cpp and .h file should #include every header file it directly uses classes, functions or other definitions from, even if those headers are already included indirectly through other headers. https://github.com/bitcoin/bitcoin/blob/master/doc/developer-notes.md#source-code-organization


    marcinja commented at 7:01 PM on August 11, 2018:

    Fixed.

  38. marcinja force-pushed on Aug 11, 2018
  39. MarcoFalke commented at 9:37 PM on August 12, 2018: member

    re-utACK 4b7091a842c2c3a76f4136cb0fdcf1c5904fd237

  40. kallewoof commented at 6:50 AM on August 13, 2018: member

    utACK 4b7091a842c2c3a76f4136cb0fdcf1c5904fd237

  41. ken2812221 referenced this in commit a9c56b6634 on Aug 13, 2018
  42. MarcoFalke merged this on Aug 13, 2018
  43. MarcoFalke closed this on Aug 13, 2018

  44. laanwj commented at 11:29 AM on August 13, 2018: member

    Cannot really test this without txindex enabled, utACK 4b7091a842c2c3a76f4136cb0fdcf1c5904fd237

  45. jtimon commented at 7:20 PM on August 16, 2018: contributor

    concept ACK, I wish somebody had pinged me for review.

    I think I would have liked more to keep them as separated fields (ie percentile_feerate10, percentile_feerate25, percentile_feerate50, percentile_feerate75, percentile_feerate90) instead of giving them as an array, but no big deal, I can just do that on the caller.

  46. marcinja deleted the branch on Aug 31, 2018
  47. ftrader referenced this in commit ae28a40a01 on Aug 17, 2020
  48. UdjinM6 referenced this in commit 0aa42be38d on Jul 4, 2021
  49. UdjinM6 referenced this in commit 4baa0ddc66 on Jul 6, 2021
  50. UdjinM6 referenced this in commit ae4f7f4d08 on Jul 6, 2021
  51. MarcoFalke locked this on Sep 8, 2021

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-04-13 15:15 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me