Modify gas price statistics (#2947)
* gas price distribution + median + tests * put histogram in util * use the util histogram * remove the default gas price implementation * histogram rpc * fix empty corpus * Add JS ethcore_gasPriceHistogram * Fix typo (s/types/type/) & subsequent failing test * Fix return type & formatting * bucketBounds * Add jsapi e2e test verification
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use util::{U256, Address, H256, H2048, Bytes, Itertools};
|
||||
use util::stats::Histogram;
|
||||
use blockchain::TreeRoute;
|
||||
use verification::queue::QueueInfo as BlockQueueInfo;
|
||||
use block::{OpenBlock, SealedBlock};
|
||||
@@ -190,8 +191,8 @@ pub trait BlockChainClient : Sync + Send {
|
||||
/// list all transactions
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction>;
|
||||
|
||||
/// Get the gas price distribution.
|
||||
fn gas_price_statistics(&self, sample_size: usize, distribution_size: usize) -> Result<Vec<U256>, ()> {
|
||||
/// Sorted list of transaction gas prices from at least last sample_size blocks.
|
||||
fn gas_price_corpus(&self, sample_size: usize) -> Vec<U256> {
|
||||
let mut h = self.chain_info().best_block_hash;
|
||||
let mut corpus = Vec::new();
|
||||
while corpus.is_empty() {
|
||||
@@ -200,25 +201,29 @@ pub trait BlockChainClient : Sync + Send {
|
||||
let block = BlockView::new(&block_bytes);
|
||||
let header = block.header_view();
|
||||
if header.number() == 0 {
|
||||
if corpus.is_empty() {
|
||||
corpus.push(20_000_000_000u64.into()); // we have literally no information - it' as good a number as any.
|
||||
}
|
||||
break;
|
||||
return corpus;
|
||||
}
|
||||
block.transaction_views().iter().foreach(|t| corpus.push(t.gas_price()));
|
||||
h = header.parent_hash().clone();
|
||||
}
|
||||
}
|
||||
corpus.sort();
|
||||
let n = corpus.len();
|
||||
if n > 0 {
|
||||
Ok((0..(distribution_size + 1))
|
||||
.map(|i| corpus[i * (n - 1) / distribution_size])
|
||||
.collect::<Vec<_>>()
|
||||
)
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
corpus
|
||||
}
|
||||
|
||||
/// Calculate median gas price from recent blocks if they have any transactions.
|
||||
fn gas_price_median(&self, sample_size: usize) -> Option<U256> {
|
||||
let corpus = self.gas_price_corpus(sample_size);
|
||||
corpus.get(corpus.len()/2).cloned()
|
||||
}
|
||||
|
||||
/// Get the gas price distribution based on recent blocks if they have any transactions.
|
||||
fn gas_price_histogram(&self, sample_size: usize, bucket_number: usize) -> Option<Histogram> {
|
||||
let raw_corpus = self.gas_price_corpus(sample_size);
|
||||
let raw_len = raw_corpus.len();
|
||||
// Throw out outliers.
|
||||
let (corpus, _) = raw_corpus.split_at(raw_len-raw_len/40);
|
||||
Histogram::new(corpus, bucket_number)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ pub trait MinerService : Send + Sync {
|
||||
fn is_sealing(&self) -> bool;
|
||||
|
||||
/// Suggested gas price.
|
||||
fn sensible_gas_price(&self) -> U256 { 20000000000u64.into() }
|
||||
fn sensible_gas_price(&self) -> U256;
|
||||
|
||||
/// Suggested gas limit.
|
||||
fn sensible_gas_limit(&self) -> U256 { 21000.into() }
|
||||
|
||||
@@ -26,6 +26,7 @@ use miner::Miner;
|
||||
use rlp::{Rlp, View};
|
||||
use spec::Spec;
|
||||
use views::BlockView;
|
||||
use util::stats::Histogram;
|
||||
|
||||
#[test]
|
||||
fn imports_from_empty() {
|
||||
@@ -198,19 +199,37 @@ fn can_collect_garbage() {
|
||||
assert!(client.blockchain_cache_info().blocks < 100 * 1024);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(feature="dev", allow(useless_vec))]
|
||||
fn can_generate_gas_price_statistics() {
|
||||
let client_result = generate_dummy_client_with_data(16, 1, &vec_into![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
|
||||
fn can_generate_gas_price_median() {
|
||||
let client_result = generate_dummy_client_with_data(3, 1, &vec_into![1, 2, 3]);
|
||||
let client = client_result.reference();
|
||||
let s = client.gas_price_statistics(8, 8).unwrap();
|
||||
assert_eq!(s, vec_into![8, 8, 9, 10, 11, 12, 13, 14, 15]);
|
||||
let s = client.gas_price_statistics(16, 8).unwrap();
|
||||
assert_eq!(s, vec_into![0, 1, 3, 5, 7, 9, 11, 13, 15]);
|
||||
let s = client.gas_price_statistics(32, 8).unwrap();
|
||||
assert_eq!(s, vec_into![0, 1, 3, 5, 7, 9, 11, 13, 15]);
|
||||
assert_eq!(Some(U256::from(2)), client.gas_price_median(3));
|
||||
|
||||
let client_result = generate_dummy_client_with_data(4, 1, &vec_into![1, 4, 3, 2]);
|
||||
let client = client_result.reference();
|
||||
assert_eq!(Some(U256::from(3)), client.gas_price_median(4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_generate_gas_price_histogram() {
|
||||
let client_result = generate_dummy_client_with_data(20, 1, &vec_into![6354,8593,6065,4842,7845,7002,689,4958,4250,6098,5804,4320,643,8895,2296,8589,7145,2000,2512,1408]);
|
||||
let client = client_result.reference();
|
||||
|
||||
let hist = client.gas_price_histogram(20, 5).unwrap();
|
||||
let correct_hist = Histogram { bucket_bounds: vec_into![643,2293,3943,5593,7243,8893], counts: vec![4,2,4,6,3] };
|
||||
assert_eq!(hist, correct_hist);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_gas_price_histogram() {
|
||||
let client_result = generate_dummy_client_with_data(20, 0, &vec_into![]);
|
||||
let client = client_result.reference();
|
||||
|
||||
assert!(client.gas_price_histogram(20, 5).is_none());
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn can_handle_long_fork() {
|
||||
let client_result = generate_dummy_client(1200);
|
||||
|
||||
Reference in New Issue
Block a user