implement light dispatcher
This commit is contained in:
parent
53774d4792
commit
e53d023a8a
@ -29,6 +29,7 @@ use util::sha3::Hashable;
|
|||||||
|
|
||||||
use ethkey::Signature;
|
use ethkey::Signature;
|
||||||
use ethsync::LightSync;
|
use ethsync::LightSync;
|
||||||
|
use ethcore::ids::BlockId;
|
||||||
use ethcore::miner::MinerService;
|
use ethcore::miner::MinerService;
|
||||||
use ethcore::client::MiningBlockChainClient;
|
use ethcore::client::MiningBlockChainClient;
|
||||||
use ethcore::transaction::{Action, SignedTransaction, PendingTransaction, Transaction};
|
use ethcore::transaction::{Action, SignedTransaction, PendingTransaction, Transaction};
|
||||||
@ -58,7 +59,7 @@ pub trait Dispatcher: Send + Sync + Clone {
|
|||||||
-> BoxFuture<FilledTransactionRequest, Error>;
|
-> BoxFuture<FilledTransactionRequest, Error>;
|
||||||
|
|
||||||
/// Sign the given transaction request without dispatching, fetching appropriate nonce.
|
/// Sign the given transaction request without dispatching, fetching appropriate nonce.
|
||||||
fn sign(&self, accounts: &AccountProvider, filled: FilledTransactionRequest, password: SignWith)
|
fn sign(&self, accounts: Arc<AccountProvider>, filled: FilledTransactionRequest, password: SignWith)
|
||||||
-> BoxFuture<WithToken<SignedTransaction>, Error>;
|
-> BoxFuture<WithToken<SignedTransaction>, Error>;
|
||||||
|
|
||||||
/// "Dispatch" a local transaction.
|
/// "Dispatch" a local transaction.
|
||||||
@ -111,7 +112,7 @@ impl<C: MiningBlockChainClient, M: MinerService> Dispatcher for FullDispatcher<C
|
|||||||
}).boxed()
|
}).boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign(&self, accounts: &AccountProvider, filled: FilledTransactionRequest, password: SignWith)
|
fn sign(&self, accounts: Arc<AccountProvider>, filled: FilledTransactionRequest, password: SignWith)
|
||||||
-> BoxFuture<WithToken<SignedTransaction>, Error>
|
-> BoxFuture<WithToken<SignedTransaction>, Error>
|
||||||
{
|
{
|
||||||
let (client, miner) = (take_weakf!(self.client), take_weakf!(self.miner));
|
let (client, miner) = (take_weakf!(self.client), take_weakf!(self.miner));
|
||||||
@ -133,7 +134,7 @@ impl<C: MiningBlockChainClient, M: MinerService> Dispatcher for FullDispatcher<C
|
|||||||
};
|
};
|
||||||
|
|
||||||
let hash = t.hash(network_id);
|
let hash = t.hash(network_id);
|
||||||
let signature = try_bf!(signature(accounts, address, hash, password));
|
let signature = try_bf!(signature(&accounts, address, hash, password));
|
||||||
|
|
||||||
signature.map(|sig| {
|
signature.map(|sig| {
|
||||||
SignedTransaction::new(t.with_signature(sig, network_id))
|
SignedTransaction::new(t.with_signature(sig, network_id))
|
||||||
@ -180,6 +181,85 @@ impl LightDispatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Dispatcher for LightDispatcher {
|
||||||
|
fn fill_optional_fields(&self, request: TransactionRequest, default_sender: Address)
|
||||||
|
-> BoxFuture<FilledTransactionRequest, Error>
|
||||||
|
{
|
||||||
|
let request = request;
|
||||||
|
let gas_limit = self.client.block_header(BlockId::Latest)
|
||||||
|
.expect("Best block header always kept; qed").gas_limit();
|
||||||
|
|
||||||
|
future::ok(FilledTransactionRequest {
|
||||||
|
from: request.from.unwrap_or(default_sender),
|
||||||
|
used_default_from: request.from.is_none(),
|
||||||
|
to: request.to,
|
||||||
|
nonce: request.nonce,
|
||||||
|
gas_price: request.gas_price.unwrap_or_else(|| 21_000_000.into()), // TODO: fetch corpus from network.
|
||||||
|
gas: request.gas.unwrap_or_else(|| gas_limit / 3.into()),
|
||||||
|
value: request.value.unwrap_or_else(|| 0.into()),
|
||||||
|
data: request.data.unwrap_or_else(Vec::new),
|
||||||
|
condition: request.condition,
|
||||||
|
}).boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sign(&self, accounts: Arc<AccountProvider>, filled: FilledTransactionRequest, password: SignWith)
|
||||||
|
-> BoxFuture<WithToken<SignedTransaction>, Error>
|
||||||
|
{
|
||||||
|
let network_id = None; // TODO: fetch from client.
|
||||||
|
let address = filled.from;
|
||||||
|
let best_header = self.client.block_header(BlockId::Latest)
|
||||||
|
.expect("Best block header always kept; qed");
|
||||||
|
|
||||||
|
let with_nonce = move |filled: FilledTransactionRequest, nonce| {
|
||||||
|
let t = Transaction {
|
||||||
|
nonce: nonce,
|
||||||
|
action: filled.to.map_or(Action::Create, Action::Call),
|
||||||
|
gas: filled.gas,
|
||||||
|
gas_price: filled.gas_price,
|
||||||
|
value: filled.value,
|
||||||
|
data: filled.data,
|
||||||
|
};
|
||||||
|
let hash = t.hash(network_id);
|
||||||
|
let signature = signature(&accounts, address, hash, password)?;
|
||||||
|
|
||||||
|
Ok(signature.map(|sig| {
|
||||||
|
SignedTransaction::new(t.with_signature(sig, network_id))
|
||||||
|
.expect("Transaction was signed by AccountsProvider; it never produces invalid signatures; qed")
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
|
||||||
|
// fast path where we don't go to network; nonce provided or can be gotten from queue.
|
||||||
|
let maybe_nonce = filled.nonce.or_else(|| self.transaction_queue.read().next_nonce(&address));
|
||||||
|
if let Some(nonce) = maybe_nonce {
|
||||||
|
return future::done(with_nonce(filled, nonce)).boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
let nonce_future = self.sync.with_context(|ctx| self.on_demand.account(ctx, request::Account {
|
||||||
|
header: best_header,
|
||||||
|
address: address,
|
||||||
|
}));
|
||||||
|
|
||||||
|
let nonce_future = match nonce_future {
|
||||||
|
Some(x) => x,
|
||||||
|
None => return future::err(errors::no_light_peers()).boxed()
|
||||||
|
};
|
||||||
|
|
||||||
|
nonce_future
|
||||||
|
.map_err(|_| errors::no_light_peers())
|
||||||
|
.and_then(move |acc| with_nonce(filled, acc.nonce + U256::one()))
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dispatch_transaction(&self, signed_transaction: PendingTransaction) -> Result<H256, Error> {
|
||||||
|
let hash = signed_transaction.transaction.hash();
|
||||||
|
|
||||||
|
self.transaction_queue.write().import(signed_transaction)
|
||||||
|
.map_err(Into::into)
|
||||||
|
.map_err(errors::from_transaction_error)
|
||||||
|
.map(|_| hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// default MAC to use.
|
/// default MAC to use.
|
||||||
pub const DEFAULT_MAC: [u8; 2] = [0, 0];
|
pub const DEFAULT_MAC: [u8; 2] = [0, 0];
|
||||||
|
|
||||||
@ -264,7 +344,7 @@ impl<T: Debug> From<(T, Option<AccountToken>)> for WithToken<T> {
|
|||||||
/// Execute a confirmation payload.
|
/// Execute a confirmation payload.
|
||||||
pub fn execute<D: Dispatcher + 'static>(
|
pub fn execute<D: Dispatcher + 'static>(
|
||||||
dispatcher: D,
|
dispatcher: D,
|
||||||
accounts: &AccountProvider,
|
accounts: Arc<AccountProvider>,
|
||||||
payload: ConfirmationPayload,
|
payload: ConfirmationPayload,
|
||||||
pass: SignWith
|
pass: SignWith
|
||||||
) -> BoxFuture<WithToken<ConfirmationResponse>, Error> {
|
) -> BoxFuture<WithToken<ConfirmationResponse>, Error> {
|
||||||
@ -294,7 +374,7 @@ pub fn execute<D: Dispatcher + 'static>(
|
|||||||
format!("\x19Ethereum Signed Message:\n{}", data.len())
|
format!("\x19Ethereum Signed Message:\n{}", data.len())
|
||||||
.into_bytes();
|
.into_bytes();
|
||||||
message_data.append(&mut data);
|
message_data.append(&mut data);
|
||||||
let res = signature(accounts, address, message_data.sha3(), pass)
|
let res = signature(&accounts, address, message_data.sha3(), pass)
|
||||||
.map(|result| result
|
.map(|result| result
|
||||||
.map(|rsv| {
|
.map(|rsv| {
|
||||||
let mut vrs = [0u8; 65];
|
let mut vrs = [0u8; 65];
|
||||||
@ -310,7 +390,7 @@ pub fn execute<D: Dispatcher + 'static>(
|
|||||||
future::done(res).boxed()
|
future::done(res).boxed()
|
||||||
},
|
},
|
||||||
ConfirmationPayload::Decrypt(address, data) => {
|
ConfirmationPayload::Decrypt(address, data) => {
|
||||||
let res = decrypt(accounts, address, data, pass)
|
let res = decrypt(&accounts, address, data, pass)
|
||||||
.map(|result| result
|
.map(|result| result
|
||||||
.map(RpcBytes)
|
.map(RpcBytes)
|
||||||
.map(ConfirmationResponse::Decrypt)
|
.map(ConfirmationResponse::Decrypt)
|
||||||
|
@ -49,6 +49,7 @@ mod codes {
|
|||||||
pub const COMPILATION_ERROR: i64 = -32050;
|
pub const COMPILATION_ERROR: i64 = -32050;
|
||||||
pub const ENCRYPTION_ERROR: i64 = -32055;
|
pub const ENCRYPTION_ERROR: i64 = -32055;
|
||||||
pub const FETCH_ERROR: i64 = -32060;
|
pub const FETCH_ERROR: i64 = -32060;
|
||||||
|
pub const NO_LIGHT_PEERS: i64 = -32065;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unimplemented(details: Option<String>) -> Error {
|
pub fn unimplemented(details: Option<String>) -> Error {
|
||||||
@ -308,3 +309,11 @@ pub fn unknown_block() -> Error {
|
|||||||
data: None,
|
data: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn no_light_peers() -> Error {
|
||||||
|
Error {
|
||||||
|
code: ErrorCode::ServerError(codes::NO_LIGHT_PEERS),
|
||||||
|
message: "No light peers who can serve data".into(),
|
||||||
|
data: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -113,7 +113,7 @@ impl<D: Dispatcher + 'static> Personal for PersonalClient<D> {
|
|||||||
dispatcher.fill_optional_fields(request.into(), default)
|
dispatcher.fill_optional_fields(request.into(), default)
|
||||||
.and_then(move |filled| {
|
.and_then(move |filled| {
|
||||||
let condition = filled.condition.clone().map(Into::into);
|
let condition = filled.condition.clone().map(Into::into);
|
||||||
dispatcher.sign(&accounts, filled, SignWith::Password(password))
|
dispatcher.sign(accounts, filled, SignWith::Password(password))
|
||||||
.map(|tx| tx.into_value())
|
.map(|tx| tx.into_value())
|
||||||
.map(move |tx| PendingTransaction::new(tx, condition))
|
.map(move |tx| PendingTransaction::new(tx, condition))
|
||||||
.map(move |tx| (tx, dispatcher))
|
.map(move |tx| (tx, dispatcher))
|
||||||
|
@ -52,7 +52,7 @@ impl<D: Dispatcher + 'static> SignerClient<D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn confirm_internal<F, T>(&self, id: U256, modification: TransactionModification, f: F) -> BoxFuture<WithToken<ConfirmationResponse>, Error> where
|
fn confirm_internal<F, T>(&self, id: U256, modification: TransactionModification, f: F) -> BoxFuture<WithToken<ConfirmationResponse>, Error> where
|
||||||
F: FnOnce(D, &AccountProvider, ConfirmationPayload) -> T,
|
F: FnOnce(D, Arc<AccountProvider>, ConfirmationPayload) -> T,
|
||||||
T: IntoFuture<Item=WithToken<ConfirmationResponse>, Error=Error>,
|
T: IntoFuture<Item=WithToken<ConfirmationResponse>, Error=Error>,
|
||||||
T::Future: Send + 'static
|
T::Future: Send + 'static
|
||||||
{
|
{
|
||||||
@ -87,7 +87,7 @@ impl<D: Dispatcher + 'static> SignerClient<D> {
|
|||||||
request.condition = condition.clone().map(Into::into);
|
request.condition = condition.clone().map(Into::into);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let fut = f(dispatcher, &*accounts, payload);
|
let fut = f(dispatcher, accounts, payload);
|
||||||
fut.into_future().then(move |result| {
|
fut.into_future().then(move |result| {
|
||||||
// Execute
|
// Execute
|
||||||
if let Ok(ref response) = result {
|
if let Ok(ref response) = result {
|
||||||
|
@ -94,7 +94,7 @@ impl<D: Dispatcher + 'static> SigningQueueClient<D> {
|
|||||||
.and_then(move |payload| {
|
.and_then(move |payload| {
|
||||||
let sender = payload.sender();
|
let sender = payload.sender();
|
||||||
if accounts.is_unlocked(sender) {
|
if accounts.is_unlocked(sender) {
|
||||||
dispatch::execute(dispatcher, &accounts, payload, dispatch::SignWith::Nothing)
|
dispatch::execute(dispatcher, accounts, payload, dispatch::SignWith::Nothing)
|
||||||
.map(|v| v.into_value())
|
.map(|v| v.into_value())
|
||||||
.map(DispatchResult::Value)
|
.map(DispatchResult::Value)
|
||||||
.boxed()
|
.boxed()
|
||||||
|
@ -61,7 +61,7 @@ impl<D: Dispatcher + 'static> SigningUnsafeClient<D> {
|
|||||||
let dis = self.dispatcher.clone();
|
let dis = self.dispatcher.clone();
|
||||||
dispatch::from_rpc(payload, default, &dis)
|
dispatch::from_rpc(payload, default, &dis)
|
||||||
.and_then(move |payload| {
|
.and_then(move |payload| {
|
||||||
dispatch::execute(dis, &accounts, payload, dispatch::SignWith::Nothing)
|
dispatch::execute(dis, accounts, payload, dispatch::SignWith::Nothing)
|
||||||
})
|
})
|
||||||
.map(|v| v.into_value())
|
.map(|v| v.into_value())
|
||||||
.boxed()
|
.boxed()
|
||||||
|
Loading…
Reference in New Issue
Block a user