Optional gas price in transactions come from statistics (#1388)

* use gas price statistics for default transaction gas price

* create new signing queue client properly

* replace one more usage of sensible_gas_price

* fill_optional_fields as a free function

* keep test client alive
This commit is contained in:
Robert Habermeier 2016-06-22 15:55:07 +02:00 committed by Gav Wood
parent 78cc5a6ed1
commit f947a9cb71
5 changed files with 48 additions and 37 deletions

View File

@ -150,7 +150,7 @@ pub fn setup_rpc<T: Extendable>(server: T, deps: Arc<Dependencies>, apis: ApiSet
server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate()); server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate());
if deps.signer_port.is_some() { if deps.signer_port.is_some() {
server.add_delegate(EthSigningQueueClient::new(&deps.signer_queue, &deps.miner).to_delegate()); server.add_delegate(EthSigningQueueClient::new(&deps.signer_queue, &deps.client, &deps.miner).to_delegate());
} else { } else {
server.add_delegate(EthSigningUnsafeClient::new(&deps.client, &deps.secret_store, &deps.miner).to_delegate()); server.add_delegate(EthSigningUnsafeClient::new(&deps.client, &deps.secret_store, &deps.miner).to_delegate());
} }

View File

@ -37,7 +37,7 @@ use ethcore::filter::Filter as EthcoreFilter;
use self::ethash::SeedHashCompute; use self::ethash::SeedHashCompute;
use v1::traits::Eth; use v1::traits::Eth;
use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, CallRequest, OptionalValue, Index, Filter, Log, Receipt}; use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, CallRequest, OptionalValue, Index, Filter, Log, Receipt};
use v1::impls::{dispatch_transaction, error_codes}; use v1::impls::{default_gas_price, dispatch_transaction, error_codes};
use serde; use serde;
/// Eth rpc implementation. /// Eth rpc implementation.
@ -153,23 +153,14 @@ impl<C, S, M, EM> EthClient<C, S, 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> { fn sign_call(&self, request: CallRequest) -> Result<SignedTransaction, Error> {
let client = take_weak!(self.client); let (client, miner) = (take_weak!(self.client), take_weak!(self.miner));
let from = request.from.unwrap_or(Address::zero()); let from = request.from.unwrap_or(Address::zero());
Ok(EthTransaction { Ok(EthTransaction {
nonce: request.nonce.unwrap_or_else(|| client.latest_nonce(&from)), nonce: request.nonce.unwrap_or_else(|| client.latest_nonce(&from)),
action: request.to.map_or(Action::Create, Action::Call), action: request.to.map_or(Action::Create, Action::Call),
gas: request.gas.unwrap_or(U256::from(50_000_000)), gas: request.gas.unwrap_or(U256::from(50_000_000)),
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")), gas_price: request.gas_price.unwrap_or_else(|| default_gas_price(&*client, &*miner)),
value: request.value.unwrap_or_else(U256::zero), value: request.value.unwrap_or_else(U256::zero),
data: request.data.map_or_else(Vec::new, |d| d.to_vec()) data: request.data.map_or_else(Vec::new, |d| d.to_vec())
}.fake_sign(from)) }.fake_sign(from))
@ -296,7 +287,10 @@ impl<C, S, M, EM> Eth for EthClient<C, S, M, EM> where
fn gas_price(&self, params: Params) -> Result<Value, Error> { fn gas_price(&self, params: Params) -> Result<Value, Error> {
match params { match params {
Params::None => to_value(&try!(self.default_gas_price())), Params::None => {
let (client, miner) = (take_weak!(self.client), take_weak!(self.miner));
to_value(&default_gas_price(&*client, &*miner))
}
_ => Err(Error::invalid_params()) _ => Err(Error::invalid_params())
} }
} }

View File

@ -25,38 +25,42 @@ use ethcore::account_provider::AccountProvider;
use v1::helpers::{SigningQueue, ConfirmationsQueue}; use v1::helpers::{SigningQueue, ConfirmationsQueue};
use v1::traits::EthSigning; use v1::traits::EthSigning;
use v1::types::{TransactionRequest, Bytes}; use v1::types::{TransactionRequest, Bytes};
use v1::impls::sign_and_dispatch; use v1::impls::{default_gas_price, sign_and_dispatch};
fn fill_optional_fields<C, M>(request: &mut TransactionRequest, client: &C, miner: &M)
where C: MiningBlockChainClient, M: MinerService {
if request.gas.is_none() {
request.gas = Some(miner.sensible_gas_limit());
}
if request.gas_price.is_none() {
request.gas_price = Some(default_gas_price(client, miner));
}
if request.data.is_none() {
request.data = Some(Bytes::new(Vec::new()));
}
}
/// Implementation of functions that require signing when no trusted signer is used. /// Implementation of functions that require signing when no trusted signer is used.
pub struct EthSigningQueueClient<M: MinerService> { pub struct EthSigningQueueClient<C, M> where C: MiningBlockChainClient, M: MinerService {
queue: Weak<ConfirmationsQueue>, queue: Weak<ConfirmationsQueue>,
client: Weak<C>,
miner: Weak<M>, miner: Weak<M>,
} }
impl<M: MinerService> EthSigningQueueClient<M> { impl<C, M> EthSigningQueueClient<C, M> where C: MiningBlockChainClient, M: MinerService {
/// Creates a new signing queue client given shared signing queue. /// Creates a new signing queue client given shared signing queue.
pub fn new(queue: &Arc<ConfirmationsQueue>, miner: &Arc<M>) -> Self { pub fn new(queue: &Arc<ConfirmationsQueue>, client: &Arc<C>, miner: &Arc<M>) -> Self {
EthSigningQueueClient { EthSigningQueueClient {
queue: Arc::downgrade(queue), queue: Arc::downgrade(queue),
client: Arc::downgrade(client),
miner: Arc::downgrade(miner), miner: Arc::downgrade(miner),
} }
} }
fn fill_optional_fields(&self, miner: Arc<M>, mut request: TransactionRequest) -> TransactionRequest {
if let None = request.gas {
request.gas = Some(miner.sensible_gas_limit());
}
if let None = request.gas_price {
request.gas_price = Some(miner.sensible_gas_price());
}
if let None = request.data {
request.data = Some(Bytes::new(Vec::new()));
}
request
}
} }
impl<M: MinerService + 'static> EthSigning for EthSigningQueueClient<M> { impl<C, M> EthSigning for EthSigningQueueClient<C, M>
where C: MiningBlockChainClient + 'static, M: MinerService + 'static
{
fn sign(&self, _params: Params) -> Result<Value, Error> { fn sign(&self, _params: Params) -> Result<Value, Error> {
warn!("Invoking eth_sign is not yet supported with signer enabled."); warn!("Invoking eth_sign is not yet supported with signer enabled.");
@ -66,10 +70,11 @@ impl<M: MinerService + 'static> EthSigning for EthSigningQueueClient<M> {
fn send_transaction(&self, params: Params) -> Result<Value, Error> { fn send_transaction(&self, params: Params) -> Result<Value, Error> {
from_params::<(TransactionRequest, )>(params) from_params::<(TransactionRequest, )>(params)
.and_then(|(request, )| { .and_then(|(mut request, )| {
let queue = take_weak!(self.queue); let queue = take_weak!(self.queue);
let miner = take_weak!(self.miner); let (client, miner) = (take_weak!(self.client), take_weak!(self.miner));
let request = self.fill_optional_fields(miner, request);
fill_optional_fields(&mut request, &*client, &*miner);
let id = queue.add_request(request); let id = queue.add_request(request);
let result = id.wait_with_timeout(); let result = id.wait_with_timeout();
result.unwrap_or_else(|| to_value(&H256::new())) result.unwrap_or_else(|| to_value(&H256::new()))

View File

@ -100,7 +100,7 @@ fn prepare_transaction<C, M>(client: &C, miner: &M, request: TransactionRequest)
action: request.to.map_or(Action::Create, Action::Call), action: request.to.map_or(Action::Create, Action::Call),
gas: request.gas.unwrap_or_else(|| miner.sensible_gas_limit()), gas: request.gas.unwrap_or_else(|| miner.sensible_gas_limit()),
gas_price: request.gas_price.unwrap_or_else(|| miner.sensible_gas_price()), gas_price: request.gas_price.unwrap_or_else(|| default_gas_price(client, miner)),
value: request.value.unwrap_or_else(U256::zero), value: request.value.unwrap_or_else(U256::zero),
data: request.data.map_or_else(Vec::new, |b| b.to_vec()), data: request.data.map_or_else(Vec::new, |b| b.to_vec()),
} }
@ -134,6 +134,14 @@ fn sign_and_dispatch<C, M>(client: &C, miner: &M, request: TransactionRequest, a
dispatch_transaction(&*client, &*miner, signed_transaction) dispatch_transaction(&*client, &*miner, signed_transaction)
} }
fn default_gas_price<C, M>(client: &C, miner: &M) -> U256 where C: MiningBlockChainClient, M: MinerService {
client
.gas_price_statistics(100, 8)
.map(|x| x[4])
.unwrap_or_else(|_| miner.sensible_gas_price())
}
fn signing_error(error: AccountError) -> Error { fn signing_error(error: AccountError) -> Error {
Error { Error {
code: ErrorCode::ServerError(error_codes::ACCOUNT_LOCKED), code: ErrorCode::ServerError(error_codes::ACCOUNT_LOCKED),

View File

@ -21,9 +21,11 @@ use v1::traits::EthSigning;
use v1::helpers::{ConfirmationsQueue, SigningQueue}; use v1::helpers::{ConfirmationsQueue, SigningQueue};
use v1::tests::helpers::TestMinerService; use v1::tests::helpers::TestMinerService;
use util::{Address, FixedHash}; use util::{Address, FixedHash};
use ethcore::client::TestBlockChainClient;
struct EthSigningTester { struct EthSigningTester {
pub queue: Arc<ConfirmationsQueue>, pub queue: Arc<ConfirmationsQueue>,
pub client: Arc<TestBlockChainClient>,
pub miner: Arc<TestMinerService>, pub miner: Arc<TestMinerService>,
pub io: IoHandler, pub io: IoHandler,
} }
@ -31,12 +33,14 @@ struct EthSigningTester {
impl Default for EthSigningTester { impl Default for EthSigningTester {
fn default() -> Self { fn default() -> Self {
let queue = Arc::new(ConfirmationsQueue::default()); let queue = Arc::new(ConfirmationsQueue::default());
let client = Arc::new(TestBlockChainClient::default());
let miner = Arc::new(TestMinerService::default()); let miner = Arc::new(TestMinerService::default());
let io = IoHandler::new(); let io = IoHandler::new();
io.add_delegate(EthSigningQueueClient::new(&queue, &miner).to_delegate()); io.add_delegate(EthSigningQueueClient::new(&queue, &client, &miner).to_delegate());
EthSigningTester { EthSigningTester {
queue: queue, queue: queue,
client: client,
miner: miner, miner: miner,
io: io, io: io,
} }