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

import rpc transactions sequentially #10051

Merged
merged 7 commits into from
Jan 22, 2019
Merged
Show file tree
Hide file tree
Changes from 3 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
123 changes: 98 additions & 25 deletions rpc/src/v1/helpers/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,14 @@ use jsonrpc_core::futures::{future, Future, Poll, Async};
use jsonrpc_core::futures::future::Either;
use v1::helpers::{errors, nonce, TransactionRequest, FilledTransactionRequest, ConfirmationPayload};
use v1::types::{
H256 as RpcH256, H520 as RpcH520, Bytes as RpcBytes,
H520 as RpcH520, Bytes as RpcBytes,
RichRawTransaction as RpcRichRawTransaction,
ConfirmationPayload as RpcConfirmationPayload,
ConfirmationResponse,
EthSignRequest as RpcEthSignRequest,
EIP191SignRequest as RpcSignRequest,
DecryptRequest as RpcDecryptRequest,
TransactionCondition
};
use rlp;

Expand All @@ -69,9 +70,14 @@ pub trait Dispatcher: Send + Sync + Clone {
fn fill_optional_fields(&self, request: TransactionRequest, default_sender: Address, force_nonce: bool)
-> BoxFuture<FilledTransactionRequest>;

/// Sign the given transaction request without dispatching, fetching appropriate nonce.
fn sign(&self, accounts: Arc<AccountProvider>, filled: FilledTransactionRequest, password: SignWith)
-> BoxFuture<WithToken<SignedTransaction>>;
/// Sign the given transaction request, fetching appropriate nonce and executing the PostSign action
fn sign(
&self,
accounts: Arc<AccountProvider>,
filled: FilledTransactionRequest,
password: SignWith,
post_sign: impl PostSign + 'static
) -> BoxFuture<WithToken<SignedTransaction>>;

/// Converts a `SignedTransaction` into `RichRawTransaction`
fn enrich(&self, SignedTransaction) -> RpcRichRawTransaction;
Expand Down Expand Up @@ -164,8 +170,13 @@ impl<C: miner::BlockChainClient + BlockChainClient, M: MinerService> Dispatcher
}))
}

fn sign(&self, accounts: Arc<AccountProvider>, filled: FilledTransactionRequest, password: SignWith)
-> BoxFuture<WithToken<SignedTransaction>>
fn sign(
&self,
accounts: Arc<AccountProvider>,
filled: FilledTransactionRequest,
password: SignWith,
post_sign: impl PostSign + 'static
) -> BoxFuture<WithToken<SignedTransaction>>
{
let chain_id = self.client.signing_chain_id();

Expand All @@ -176,7 +187,10 @@ impl<C: miner::BlockChainClient + BlockChainClient, M: MinerService> Dispatcher
let state = self.state_nonce(&filled.from);
let reserved = self.nonces.lock().reserve(filled.from, state);

Box::new(ProspectiveSigner::new(accounts, filled, chain_id, reserved, password))
Box::new(WithPostSign {
signer: ProspectiveSigner::new(accounts, filled, chain_id, reserved, password),
post_sign
})
}

fn enrich(&self, signed_transaction: SignedTransaction) -> RpcRichRawTransaction {
Expand Down Expand Up @@ -396,8 +410,13 @@ impl Dispatcher for LightDispatcher {
}))
}

fn sign(&self, accounts: Arc<AccountProvider>, filled: FilledTransactionRequest, password: SignWith)
-> BoxFuture<WithToken<SignedTransaction>>
fn sign(
&self,
accounts: Arc<AccountProvider>,
filled: FilledTransactionRequest,
password: SignWith,
_: impl PostSign + 'static
) -> BoxFuture<WithToken<SignedTransaction>>
{
let chain_id = self.client.signing_chain_id();
let nonce = filled.nonce.expect("nonce is always provided; qed");
Expand Down Expand Up @@ -464,6 +483,66 @@ struct ProspectiveSigner {
ready: Option<nonce::Ready>,
}

/// action to execute after signing
/// e.g importing a transaction into the chain
pub trait PostSign: Send + Sync {
/// perform an action with the signed transaction
fn execute(&mut self, signer: SignedTransaction) -> Result<()>;
}

/// performs no action in PostSign
pub struct NoopPostSign;

impl PostSign for NoopPostSign {
fn execute(&mut self, _: SignedTransaction) -> Result<()> {
Ok(())
}
}

/// thsis imports the signed transaction in PostSign
pub struct ImportTransactionPostSign<D: Dispatcher> {
dispatcher: D,
condition: Option<TransactionCondition>
}

impl<D: Dispatcher> ImportTransactionPostSign<D> {
/// given a dispatcher and a transaction condition
/// contructs a PostSign trait object that will import the signed transaction
pub fn new(dispatcher: D, condition: Option<TransactionCondition>) -> Self {
ImportTransactionPostSign {
dispatcher,
condition
}
}
}

impl<D: Dispatcher> PostSign for ImportTransactionPostSign<D> {
fn execute(&mut self, signed: SignedTransaction) -> Result<()> {
let condition = self.condition.take().map(Into::into);
self.dispatcher.dispatch_transaction(PendingTransaction::new(signed, condition))?;
Ok(())
}
}

struct WithPostSign<P: PostSign> {
signer: ProspectiveSigner,
post_sign: P
}

impl<P: PostSign> Future for WithPostSign<P> {
type Item = WithToken<SignedTransaction>;
type Error = Error;

fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let (signed, ready) = try_ready!(self.signer.poll());
if let Err(e) = self.post_sign.execute((*signed).clone()) {
return Err(e)
}
ready.mark_used();
return Ok(Async::Ready(signed))
}
}

impl ProspectiveSigner {
pub fn new(
accounts: Arc<AccountProvider>,
Expand Down Expand Up @@ -510,7 +589,7 @@ impl ProspectiveSigner {
}

impl Future for ProspectiveSigner {
type Item = WithToken<SignedTransaction>;
type Item = (WithToken<SignedTransaction>, nonce::Ready);
type Error = Error;

fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
Expand Down Expand Up @@ -544,10 +623,9 @@ impl Future for ProspectiveSigner {
},
Finish => {
if let (Some(result), Some(nonce)) = (self.prospective.take(), self.ready.take()) {
// Mark nonce as used on successful signing
// return nonce and signed tx
return result.map(move |tx| {
nonce.mark_used();
Async::Ready(tx)
Async::Ready((tx, nonce))
})
} else {
panic!("Poll after ready.");
Expand Down Expand Up @@ -654,20 +732,15 @@ pub fn execute<D: Dispatcher + 'static>(
) -> BoxFuture<WithToken<ConfirmationResponse>> {
match payload {
ConfirmationPayload::SendTransaction(request) => {
let condition = request.condition.clone().map(Into::into);
Box::new(dispatcher.sign(accounts, request, pass)
.map(move |v| v.map(move |tx| PendingTransaction::new(tx, condition)))
.map(WithToken::into_tuple)
.map(|(tx, token)| (tx, token, dispatcher))
.and_then(|(tx, tok, dispatcher)| {
dispatcher.dispatch_transaction(tx)
.map(RpcH256::from)
.map(ConfirmationResponse::SendTransaction)
.map(move |h| WithToken::from((h, tok)))
}))
let post_sign = ImportTransactionPostSign::new(dispatcher.clone(), request.condition.clone());
Box::new(dispatcher.sign(accounts, request, pass, post_sign)
.map(|with_token| {
with_token.map(|tx| ConfirmationResponse::SendTransaction(tx.hash().into()))
})
)
},
ConfirmationPayload::SignTransaction(request) => {
Box::new(dispatcher.sign(accounts, request, pass)
Box::new(dispatcher.sign(accounts, request, pass, NoopPostSign)
.map(move |result| result
.map(move |tx| dispatcher.enrich(tx))
.map(ConfirmationResponse::SignTransaction)
Expand Down
23 changes: 16 additions & 7 deletions rpc/src/v1/impls/personal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use ethkey::{public_to_address, recover, Signature};
use jsonrpc_core::{BoxFuture, Result};
use jsonrpc_core::futures::{future, Future};
use v1::helpers::{errors, eip191};
use v1::helpers::dispatch::{self, eth_data_hash, Dispatcher, SignWith};
use v1::helpers::dispatch::{self, eth_data_hash, Dispatcher, SignWith, PostSign, NoopPostSign, ImportTransactionPostSign};
use v1::traits::Personal;
use v1::types::{
H160 as RpcH160, H256 as RpcH256, H520 as RpcH520, U128 as RpcU128,
Expand Down Expand Up @@ -68,7 +68,15 @@ impl<D: Dispatcher> PersonalClient<D> {
}

impl<D: Dispatcher + 'static> PersonalClient<D> {
fn do_sign_transaction(&self, _meta: Metadata, request: TransactionRequest, password: String) -> BoxFuture<(PendingTransaction, D)> {
fn do_sign_transaction<T>(
&self,
_meta: Metadata,
request: TransactionRequest,
password: String,
post_sign: T
) -> BoxFuture<(PendingTransaction, D)>
where T: 'static + PostSign
{
let dispatcher = self.dispatcher.clone();
let accounts = self.accounts.clone();

Expand All @@ -87,7 +95,7 @@ impl<D: Dispatcher + 'static> PersonalClient<D> {
Box::new(dispatcher.fill_optional_fields(request.into(), default, false)
.and_then(move |filled| {
let condition = filled.condition.clone().map(Into::into);
dispatcher.sign(accounts, filled, SignWith::Password(password.into()))
dispatcher.sign(accounts, filled, SignWith::Password(password.into()), post_sign)
.map(|tx| tx.into_value())
.map(move |tx| PendingTransaction::new(tx, condition))
.map(move |tx| (tx, dispatcher))
Expand Down Expand Up @@ -223,18 +231,19 @@ impl<D: Dispatcher + 'static> Personal for PersonalClient<D> {
}

fn sign_transaction(&self, meta: Metadata, request: TransactionRequest, password: String) -> BoxFuture<RpcRichRawTransaction> {
Box::new(self.do_sign_transaction(meta, request, password)
Box::new(self.do_sign_transaction(meta, request, password, NoopPostSign)
.map(|(pending_tx, dispatcher)| dispatcher.enrich(pending_tx.transaction)))
}

fn send_transaction(&self, meta: Metadata, request: TransactionRequest, password: String) -> BoxFuture<RpcH256> {
Box::new(self.do_sign_transaction(meta, request, password)
.and_then(|(pending_tx, dispatcher)| {
let post_sign = ImportTransactionPostSign::new(self.dispatcher.clone(), request.condition.clone());
Box::new(self.do_sign_transaction(meta, request, password, post_sign)
.and_then(|(pending_tx, _)| {
let chain_id = pending_tx.chain_id();
trace!(target: "miner", "send_transaction: dispatching tx: {} for chain ID {:?}",
::rlp::encode(&*pending_tx).pretty(), chain_id);

dispatcher.dispatch_transaction(pending_tx).map(Into::into)
Ok(RpcH256::from(pending_tx.hash()))
})
)
}
Expand Down