Gas price statistics. (#1291)
* Gas price statistics. Affects eth_gasPrice. Added ethcore_gasPriceStatistics. Closes #1265 * Fix a bug in eth_gasPrice * Fix tests. * Revert minor alteration. * Tests for gas_price_statistics. - Tests; - Additional infrastructure for generating test blocks with transactions.
This commit is contained in:
33
ethcore/res/null.json
Normal file
33
ethcore/res/null.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "Morden",
|
||||
"engine": {
|
||||
"Null": null
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x2"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x00006d6f7264656e",
|
||||
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x20000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x2fefd8"
|
||||
},
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "balance": "1", "nonce": "0", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
"0000000000000000000000000000000000000002": { "balance": "1", "nonce": "0", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
|
||||
"0000000000000000000000000000000000000003": { "balance": "1", "nonce": "0", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
|
||||
"0000000000000000000000000000000000000004": { "balance": "1", "nonce": "0", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
|
||||
"9cce34f7ab185c7aba1b7c8140d620b4bda941d6": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "0" }
|
||||
}
|
||||
}
|
||||
@@ -770,8 +770,7 @@ impl<V> MiningBlockChainClient for Client<V> where V: Verifier {
|
||||
author,
|
||||
gas_floor_target,
|
||||
extra_data,
|
||||
).expect("OpenBlock::new only fails if parent state root invalid. State root of best block's header is never invalid. \
|
||||
Therefore creating an OpenBlock with the best block's header will not fail.");
|
||||
).expect("OpenBlock::new only fails if parent state root invalid; state root of best block's header is never invalid; qed");
|
||||
|
||||
// Add uncles
|
||||
self.chain
|
||||
|
||||
@@ -34,6 +34,7 @@ pub use env_info::{LastHashes, EnvInfo};
|
||||
use util::bytes::Bytes;
|
||||
use util::hash::{Address, H256, H2048};
|
||||
use util::numbers::U256;
|
||||
use util::Itertools;
|
||||
use blockchain::TreeRoute;
|
||||
use block_queue::BlockQueueInfo;
|
||||
use block::OpenBlock;
|
||||
@@ -41,6 +42,7 @@ use header::{BlockNumber, Header};
|
||||
use transaction::{LocalizedTransaction, SignedTransaction};
|
||||
use log_entry::LocalizedLogEntry;
|
||||
use filter::Filter;
|
||||
use views::BlockView;
|
||||
use error::{ImportResult, ExecutionError};
|
||||
use receipt::LocalizedReceipt;
|
||||
use trace::LocalizedTrace;
|
||||
@@ -193,6 +195,32 @@ pub trait BlockChainClient : Sync + Send {
|
||||
|
||||
/// list all transactions
|
||||
fn all_transactions(&self) -> Vec<SignedTransaction>;
|
||||
|
||||
/// Get the gas price distribution.
|
||||
fn gas_price_statistics(&self, sample_size: usize, distribution_size: usize) -> Result<Vec<U256>, ()> {
|
||||
let mut h = self.chain_info().best_block_hash;
|
||||
let mut corpus = Vec::new();
|
||||
for _ in 0..sample_size {
|
||||
let block_bytes = self.block(BlockID::Hash(h)).expect("h is either the best_block_hash or an ancestor; qed");
|
||||
let block = BlockView::new(&block_bytes);
|
||||
let header = block.header_view();
|
||||
if header.number() == 0 {
|
||||
break;
|
||||
}
|
||||
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(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extended client interface used for mining
|
||||
|
||||
@@ -244,6 +244,11 @@ impl Spec {
|
||||
pub fn new_test() -> Spec {
|
||||
Spec::load(include_bytes!("../../res/null_morden.json"))
|
||||
}
|
||||
|
||||
/// Create a new Spec which is a NullEngine consensus with a premine of address whose secret is sha3('').
|
||||
pub fn new_null() -> Spec {
|
||||
Spec::load(include_bytes!("../../res/null.json"))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -112,6 +112,18 @@ fn can_collect_garbage() {
|
||||
assert!(client.blockchain_cache_info().blocks < 100 * 1024);
|
||||
}
|
||||
|
||||
#[test]
|
||||
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]);
|
||||
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]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_handle_long_fork() {
|
||||
let client_result = generate_dummy_client(1200);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
use client::{BlockChainClient, Client, ClientConfig};
|
||||
use common::*;
|
||||
use spec::*;
|
||||
use block::{OpenBlock};
|
||||
use blockchain::{BlockChain, Config as BlockChainConfig};
|
||||
use state::*;
|
||||
use evm::Schedule;
|
||||
@@ -85,6 +86,7 @@ impl Engine for TestEngine {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: move everything over to get_null_spec.
|
||||
pub fn get_test_spec() -> Spec {
|
||||
Spec::new_test()
|
||||
}
|
||||
@@ -126,7 +128,7 @@ fn create_unverifiable_block(order: u32, parent_hash: H256) -> Bytes {
|
||||
create_test_block(&create_unverifiable_block_header(order, parent_hash))
|
||||
}
|
||||
|
||||
pub fn create_test_block_with_data(header: &Header, transactions: &[&SignedTransaction], uncles: &[Header]) -> Bytes {
|
||||
pub fn create_test_block_with_data(header: &Header, transactions: &[SignedTransaction], uncles: &[Header]) -> Bytes {
|
||||
let mut rlp = RlpStream::new_list(3);
|
||||
rlp.append(header);
|
||||
rlp.begin_list(transactions.len());
|
||||
@@ -138,33 +140,74 @@ pub fn create_test_block_with_data(header: &Header, transactions: &[&SignedTrans
|
||||
}
|
||||
|
||||
pub fn generate_dummy_client(block_number: u32) -> GuardedTempResult<Arc<Client>> {
|
||||
generate_dummy_client_with_spec_and_data(Spec::new_test, block_number, 0, &(vec![]))
|
||||
}
|
||||
|
||||
pub fn generate_dummy_client_with_data(block_number: u32, txs_per_block: usize, tx_gas_prices: &[U256]) -> GuardedTempResult<Arc<Client>> {
|
||||
generate_dummy_client_with_spec_and_data(Spec::new_null, block_number, txs_per_block, tx_gas_prices)
|
||||
}
|
||||
|
||||
pub fn generate_dummy_client_with_spec_and_data<F>(get_test_spec: F, block_number: u32, txs_per_block: usize, tx_gas_prices: &[U256]) -> GuardedTempResult<Arc<Client>> where F: Fn()->Spec {
|
||||
let dir = RandomTempPath::new();
|
||||
|
||||
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::default()), IoChannel::disconnected()).unwrap();
|
||||
let test_spec = get_test_spec();
|
||||
let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::default()), IoChannel::disconnected()).unwrap();
|
||||
let test_engine = &test_spec.engine;
|
||||
let state_root = test_spec.genesis_header().state_root;
|
||||
let mut rolling_hash = test_spec.genesis_header().hash();
|
||||
let mut rolling_block_number = 1;
|
||||
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db = db_result.take();
|
||||
test_spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let vm_factory = Default::default();
|
||||
let genesis_header = test_spec.genesis_header();
|
||||
|
||||
let mut rolling_timestamp = 40;
|
||||
let mut last_hashes = vec![];
|
||||
let mut last_header = genesis_header.clone();
|
||||
|
||||
let kp = KeyPair::from_secret("".sha3()).unwrap() ;
|
||||
let author = kp.address();
|
||||
|
||||
let mut n = 0;
|
||||
for _ in 0..block_number {
|
||||
let mut header = Header::new();
|
||||
last_hashes.push(last_header.hash());
|
||||
|
||||
header.gas_limit = test_engine.params().min_gas_limit;
|
||||
header.difficulty = U256::from(0x20000);
|
||||
header.timestamp = rolling_timestamp;
|
||||
header.number = rolling_block_number;
|
||||
header.parent_hash = rolling_hash;
|
||||
header.state_root = state_root.clone();
|
||||
// forge block.
|
||||
let mut b = OpenBlock::new(
|
||||
test_engine.deref(),
|
||||
&vm_factory,
|
||||
false,
|
||||
db,
|
||||
&last_header,
|
||||
last_hashes.clone(),
|
||||
author.clone(),
|
||||
3141562.into(),
|
||||
vec![]
|
||||
).unwrap();
|
||||
b.set_difficulty(U256::from(0x20000));
|
||||
rolling_timestamp += 10;
|
||||
b.set_timestamp(rolling_timestamp);
|
||||
|
||||
rolling_hash = header.hash();
|
||||
rolling_block_number = rolling_block_number + 1;
|
||||
rolling_timestamp = rolling_timestamp + 10;
|
||||
// first block we don't have any balance, so can't send any transactions.
|
||||
for _ in 0..txs_per_block {
|
||||
b.push_transaction(Transaction {
|
||||
nonce: n.into(),
|
||||
gas_price: tx_gas_prices[n % tx_gas_prices.len()],
|
||||
gas: 100000.into(),
|
||||
action: Action::Create,
|
||||
data: vec![],
|
||||
value: U256::zero(),
|
||||
}.sign(kp.secret()), None).unwrap();
|
||||
n += 1;
|
||||
}
|
||||
|
||||
if let Err(e) = client.import_block(create_test_block(&header)) {
|
||||
let b = b.close_and_lock().seal(test_engine.deref(), vec![]).unwrap();
|
||||
|
||||
if let Err(e) = client.import_block(b.rlp_bytes()) {
|
||||
panic!("error importing block which is valid by definition: {:?}", e);
|
||||
}
|
||||
|
||||
last_header = BlockView::new(&b.rlp_bytes()).header();
|
||||
db = b.drain();
|
||||
}
|
||||
client.flush_queue();
|
||||
client.import_verified_blocks(&IoChannel::disconnected());
|
||||
|
||||
@@ -361,7 +361,7 @@ mod tests {
|
||||
nonce: U256::from(2)
|
||||
}.sign(&keypair.secret());
|
||||
|
||||
let good_transactions = [ &tr1, &tr2 ];
|
||||
let good_transactions = [ tr1.clone(), tr2.clone() ];
|
||||
|
||||
let diff_inc = U256::from(0x40);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user