From a559dfe9a1df05bd2e33a89f806bf84b46992460 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 9 Feb 2017 19:17:37 +0100 Subject: [PATCH] implement send_raw_transaction --- ethcore/light/src/lib.rs | 1 + ethcore/light/src/transaction_queue.rs | 58 ++++++++++++++++++++------ rpc/src/v1/impls/light/eth.rs | 23 ++++++++-- 3 files changed, 65 insertions(+), 17 deletions(-) diff --git a/ethcore/light/src/lib.rs b/ethcore/light/src/lib.rs index 7f1a85cad..94d267c7a 100644 --- a/ethcore/light/src/lib.rs +++ b/ethcore/light/src/lib.rs @@ -55,6 +55,7 @@ pub mod remote { mod types; pub use self::provider::Provider; +pub use self::transaction_queue::TransactionQueue; pub use types::les_request as request; #[macro_use] diff --git a/ethcore/light/src/transaction_queue.rs b/ethcore/light/src/transaction_queue.rs index ff530a4e7..b4f6f9ede 100644 --- a/ethcore/light/src/transaction_queue.rs +++ b/ethcore/light/src/transaction_queue.rs @@ -26,7 +26,9 @@ use std::collections::{BTreeMap, HashMap}; use std::collections::hash_map::Entry; +use ethcore::error::TransactionError; use ethcore::transaction::{Condition, PendingTransaction, SignedTransaction}; +use ethcore::transaction_import::TransactionImportResult; use util::{Address, U256, H256, H256FastMap}; // Knowledge of an account's current nonce. @@ -103,25 +105,29 @@ pub struct TransactionQueue { } impl TransactionQueue { - /// Insert a pending transaction to be queued. - pub fn insert(&mut self, tx: PendingTransaction) { + /// Import a pending transaction to be queued. + pub fn import(&mut self, tx: PendingTransaction) -> Result { let sender = tx.sender(); let hash = tx.hash(); let nonce = tx.nonce; - match self.by_account.entry(sender) { + let res = match self.by_account.entry(sender) { Entry::Vacant(entry) => { entry.insert(AccountTransactions { cur_nonce: CurrentNonce::Assumed(nonce), current: vec![tx.clone()], future: BTreeMap::new(), }); + + TransactionImportResult::Current } Entry::Occupied(mut entry) => { let acct_txs = entry.get_mut(); if &nonce < acct_txs.cur_nonce.value() { // don't accept txs from before known current nonce. - if acct_txs.cur_nonce.is_known() { return } + if acct_txs.cur_nonce.is_known() { + return Err(TransactionError::Old) + } // lower our assumption until corrected later. acct_txs.cur_nonce = CurrentNonce::Assumed(nonce); @@ -133,6 +139,8 @@ impl TransactionQueue { sender, nonce); acct_txs.current[idx] = tx.clone(); + + TransactionImportResult::Current } Err(idx) => { let cur_len = acct_txs.current.len(); @@ -153,23 +161,30 @@ impl TransactionQueue { let future_nonce = future.nonce; acct_txs.future.insert(future_nonce, future); } + + TransactionImportResult::Current } else if idx == cur_len && acct_txs.current.last().map_or(false, |f| f.nonce + 1.into() != nonce) { trace!(target: "txqueue", "Queued future transaction for {}, nonce={}", sender, nonce); let future_nonce = nonce; acct_txs.future.insert(future_nonce, tx.clone()); + + TransactionImportResult::Future } else { trace!(target: "txqueue", "Queued current transaction for {}, nonce={}", sender, nonce); // insert, then check if we've filled any gaps. acct_txs.current.insert(idx, tx.clone()); acct_txs.adjust_future(); + + TransactionImportResult::Current } } } } - } + }; self.by_hash.insert(hash, tx); + Ok(res) } /// Get pending transaction by hash. @@ -261,7 +276,7 @@ mod tests { let mut txq = TransactionQueue::default(); let tx = Transaction::default().fake_sign(sender); - txq.insert(tx.into()); + txq.import(tx.into()).unwrap(); assert_eq!(txq.queued_senders(), vec![sender]); @@ -282,7 +297,7 @@ mod tests { let tx = tx.fake_sign(sender); - txq.insert(tx.into()); + txq.import(tx.into()).unwrap(); } // current: 0..5, future: 10..15 @@ -313,7 +328,7 @@ mod tests { let tx = tx.fake_sign(sender); - txq.insert(tx.into()); + txq.import(tx.into()).unwrap(); } assert_eq!(txq.ready_transactions(0, 0).len(), 5); @@ -325,7 +340,7 @@ mod tests { let tx = tx.fake_sign(sender); - txq.insert(tx.into()); + txq.import(tx.into()).unwrap(); } assert_eq!(txq.ready_transactions(0, 0).len(), 3); @@ -337,7 +352,7 @@ mod tests { let tx = tx.fake_sign(sender); - txq.insert(tx.into()); + txq.import(tx.into()).unwrap(); } assert_eq!(txq.ready_transactions(0, 0).len(), 10); @@ -354,11 +369,11 @@ mod tests { tx.nonce = i.into(); let tx = tx.fake_sign(sender); - txq.insert(match i { + txq.import(match i { 3 => PendingTransaction::new(tx, Some(Condition::Number(100))), 4 => PendingTransaction::new(tx, Some(Condition::Timestamp(1234))), _ => tx.into(), - }); + }).unwrap(); } assert_eq!(txq.ready_transactions(0, 0).len(), 3); @@ -378,7 +393,7 @@ mod tests { let tx = tx.fake_sign(sender); - txq.insert(tx.into()); + txq.import(tx.into()).unwrap(); } txq.cull(sender, 6.into()); @@ -386,4 +401,21 @@ mod tests { assert_eq!(txq.ready_transactions(0, 0).len(), 4); assert_eq!(txq.next_nonce(&sender).unwrap(), 10.into()); } + + #[test] + fn import_old() { + let sender = Address::default(); + let mut txq = TransactionQueue::default(); + + let mut tx_a = Transaction::default(); + tx_a.nonce = 3.into(); + + let mut tx_b = Transaction::default(); + tx_b.nonce = 2.into(); + + txq.import(tx_a.fake_sign(sender).into()).unwrap(); + txq.cull(sender, 3.into()); + + assert!(txq.import(tx_b.fake_sign(sender).into()).is_err()) + } } diff --git a/rpc/src/v1/impls/light/eth.rs b/rpc/src/v1/impls/light/eth.rs index 47765bd41..614174b73 100644 --- a/rpc/src/v1/impls/light/eth.rs +++ b/rpc/src/v1/impls/light/eth.rs @@ -25,16 +25,18 @@ use jsonrpc_core::Error; use jsonrpc_macros::Trailing; use light::client::Client as LightClient; -use light::cht; +use light::{cht, TransactionQueue}; use light::on_demand::{request, OnDemand}; use ethcore::account_provider::{AccountProvider, DappId}; use ethcore::basic_account::BasicAccount; use ethcore::encoded; use ethcore::ids::BlockId; +use ethcore::transaction::SignedTransaction; use ethsync::LightSync; +use rlp::{UntrustedRlp, View}; use util::sha3::{SHA3_NULL_RLP, SHA3_EMPTY_LIST_RLP}; -use util::U256; +use util::{RwLock, U256}; use futures::{future, Future, BoxFuture}; use futures::sync::oneshot; @@ -56,6 +58,7 @@ pub struct EthClient { sync: Arc, client: Arc, on_demand: Arc, + transaction_queue: Arc>, accounts: Arc, } @@ -76,12 +79,14 @@ impl EthClient { sync: Arc, client: Arc, on_demand: Arc, + transaction_queue: Arc>, accounts: Arc, ) -> Self { EthClient { sync: sync, client: client, on_demand: on_demand, + transaction_queue: transaction_queue, accounts: accounts, } } @@ -300,11 +305,21 @@ impl Eth for EthClient { } fn send_raw_transaction(&self, raw: Bytes) -> Result { - Err(errors::unimplemented(None)) + UntrustedRlp::new(&raw.into_vec()).as_val() + .map_err(errors::from_rlp_error) + .and_then(|tx| SignedTransaction::new(tx).map_err(errors::from_transaction_error)) + .and_then(|signed| { + let hash = signed.hash(); + self.transaction_queue.write().import(signed.into()) + .map(|_| hash) + .map_err(Into::into) + .map_err(errors::from_transaction_error) + }) + .map(Into::into) } fn submit_transaction(&self, raw: Bytes) -> Result { - Err(errors::unimplemented(None)) + self.send_raw_transaction(raw) } fn call(&self, req: CallRequest, num: Trailing) -> Result {