Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Gas price statistics. #1291

Merged
merged 6 commits into from
Jun 16, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions ethcore/res/null.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any difference from from null_morden.json?

Copy link
Contributor Author

@gavofyork gavofyork Jun 16, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes - very importantly a premine at the address whose secret is sha3('').

"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" }
}
}
3 changes: 1 addition & 2 deletions ethcore/src/client/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
28 changes: 28 additions & 0 deletions ethcore/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,15 @@ 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;
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;
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions ethcore/src/spec/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
12 changes: 12 additions & 0 deletions ethcore/src/tests/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
77 changes: 60 additions & 17 deletions ethcore/src/tests/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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()
}
Expand Down Expand Up @@ -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());
Expand All @@ -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();

for _ in 0..block_number {
let mut header = Header::new();
let kp = KeyPair::from_secret("".sha3()).unwrap() ;
let author = kp.address();

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();
let mut n = 0;
for _ in 0..block_number {
last_hashes.push(last_header.hash());

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

// 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;
}

rolling_hash = header.hash();
rolling_block_number = rolling_block_number + 1;
rolling_timestamp = rolling_timestamp + 10;
let b = b.close_and_lock().seal(test_engine.deref(), vec![]).unwrap();

if let Err(e) = client.import_block(create_test_block(&header)) {
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());
Expand Down
2 changes: 1 addition & 1 deletion ethcore/src/verification/verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion parity/rpc_apis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ pub fn setup_rpc<T: Extendable>(server: T, deps: Arc<Dependencies>, apis: ApiSet
server.add_delegate(SignerClient::new(&deps.secret_store, &deps.client, &deps.miner, &deps.signer_queue).to_delegate());
},
Api::Ethcore => {
server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate())
server.add_delegate(EthcoreClient::new(&deps.client, &deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate())
},
Api::EthcoreSet => {
server.add_delegate(EthcoreSetClient::new(&deps.miner).to_delegate())
Expand Down
14 changes: 11 additions & 3 deletions rpc/src/v1/impls/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,15 +153,23 @@ impl<C, S, A, M, EM> EthClient<C, S, A, M, EM> where
}
}

fn default_gas_price(&self) -> Result<U256, Error> {
let miner = take_weak!(self.miner);
Ok(take_weak!(self.client)
.gas_price_statistics(100, 8)
.map(|x| x[4])
.unwrap_or_else(|_| miner.sensible_gas_price())
)
}

fn sign_call(&self, request: CallRequest) -> Result<SignedTransaction, Error> {
let client = take_weak!(self.client);
let miner = take_weak!(self.miner);
let from = request.from.unwrap_or(Address::zero());
Ok(EthTransaction {
nonce: request.nonce.unwrap_or_else(|| client.latest_nonce(&from)),
action: request.to.map_or(Action::Create, Action::Call),
gas: request.gas.unwrap_or(U256::from(50_000_000)),
gas_price: request.gas_price.unwrap_or_else(|| miner.sensible_gas_price()),
gas_price: request.gas_price.unwrap_or_else(|| self.default_gas_price().expect("call only fails if client or miner are unavailable; client and miner are both available to be here; qed")),
value: request.value.unwrap_or_else(U256::zero),
data: request.data.map_or_else(Vec::new, |d| d.to_vec())
}.fake_sign(from))
Expand Down Expand Up @@ -293,7 +301,7 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM> where

fn gas_price(&self, params: Params) -> Result<Value, Error> {
match params {
Params::None => to_value(&take_weak!(self.miner).sensible_gas_price()),
Params::None => to_value(&try!(self.default_gas_price())),
_ => Err(Error::invalid_params())
}
}
Expand Down
37 changes: 28 additions & 9 deletions rpc/src/v1/impls/ethcore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,42 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.

//! Ethcore-specific rpc implementation.
use util::RotatingLogger;
use util::{RotatingLogger};
use util::network_settings::NetworkSettings;
use util::misc::version_data;
use std::sync::{Arc, Weak};
use std::ops::Deref;
use std::collections::BTreeMap;
use std::collections::{BTreeMap};
use ethcore::client::{MiningBlockChainClient};
use jsonrpc_core::*;
use ethcore::miner::MinerService;
use v1::traits::Ethcore;
use v1::types::{Bytes};

/// Ethcore implementation.
pub struct EthcoreClient<M> where
pub struct EthcoreClient<C, M> where
C: MiningBlockChainClient,
M: MinerService {

client: Weak<C>,
miner: Weak<M>,
logger: Arc<RotatingLogger>,
settings: Arc<NetworkSettings>,
}

impl<M> EthcoreClient<M> where M: MinerService {
impl<C, M> EthcoreClient<C, M> where C: MiningBlockChainClient, M: MinerService {
/// Creates new `EthcoreClient`.
pub fn new(miner: &Arc<M>, logger: Arc<RotatingLogger>, settings: Arc<NetworkSettings>) -> Self {
pub fn new(client: &Arc<C>, miner: &Arc<M>, logger: Arc<RotatingLogger>, settings: Arc<NetworkSettings>) -> Self {
EthcoreClient {
client: Arc::downgrade(client),
miner: Arc::downgrade(miner),
logger: logger,
settings: settings,
}
}
}

impl<M> Ethcore for EthcoreClient<M> where M: MinerService + 'static {
impl<C, M> Ethcore for EthcoreClient<C, M> where M: MinerService + 'static, C: MiningBlockChainClient + 'static {

fn transactions_limit(&self, _: Params) -> Result<Value, Error> {
to_value(&take_weak!(self.miner).transactions_limit())
Expand Down Expand Up @@ -97,8 +101,23 @@ impl<M> Ethcore for EthcoreClient<M> where M: MinerService + 'static {
Ok(Value::Object(map))
}

fn default_extra_data(&self, _params: Params) -> Result<Value, Error> {
let version = version_data();
to_value(&Bytes::new(version))
fn default_extra_data(&self, params: Params) -> Result<Value, Error> {
match params {
Params::None => to_value(&Bytes::new(version_data())),
_ => Err(Error::invalid_params()),
}
}

fn gas_price_statistics(&self, params: Params) -> Result<Value, Error> {
match params {
Params::None => match take_weak!(self.client).gas_price_statistics(100, 8) {
Ok(stats) => to_value(&stats
.iter()
.map(|x| to_value(&x).expect("x must be U256; qed"))
.collect::<Vec<_>>()),
_ => Err(Error::internal_error()),
},
_ => Err(Error::invalid_params()),
}
}
}
Loading