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:
keorn
2016-10-31 11:57:48 +00:00
committed by Gav Wood
parent 8bf577e0fe
commit 7af20a5db0
17 changed files with 243 additions and 41 deletions

View File

@@ -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)
}
}

View File

@@ -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() }

View File

@@ -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);