diff --git a/ethcore/light/src/client/header_chain.rs b/ethcore/light/src/client/header_chain.rs index 531eaf350e1..8bfbb7743e2 100644 --- a/ethcore/light/src/client/header_chain.rs +++ b/ethcore/light/src/client/header_chain.rs @@ -206,6 +206,7 @@ impl HeaderChain { pub fn get_header(&self, id: BlockId) -> Option { match id { BlockId::Earliest | BlockId::Number(0) => Some(self.genesis_header.clone()), + BlockId::Latest if self.headers.read().is_empty() => Some(self.genesis_header.clone()), BlockId::Hash(hash) => self.headers.read().get(&hash).map(|x| x.to_vec()), BlockId::Number(num) => { if self.best_block.read().number < num { return None } diff --git a/ethcore/light/src/client/mod.rs b/ethcore/light/src/client/mod.rs index d0d8a7bb92e..c2b57be24fc 100644 --- a/ethcore/light/src/client/mod.rs +++ b/ethcore/light/src/client/mod.rs @@ -21,8 +21,9 @@ use ethcore::block_status::BlockStatus; use ethcore::client::ClientReport; use ethcore::ids::BlockId; use ethcore::header::Header; +use ethcore::views::HeaderView; use ethcore::verification::queue::{self, HeaderQueue}; -use ethcore::transaction::PendingTransaction; +use ethcore::transaction::{PendingTransaction, Condition as TransactionCondition}; use ethcore::blockchain_info::BlockChainInfo; use ethcore::spec::Spec; use ethcore::service::ClientIoMessage; @@ -34,6 +35,7 @@ use util::{Bytes, Mutex, RwLock}; use provider::Provider; use request; +use time; use self::header_chain::HeaderChain; @@ -110,7 +112,11 @@ impl Client { let best_num = self.chain.best_block().number; self.tx_pool.lock() .values() - .filter(|t| t.min_block.as_ref().map_or(true, |x| x <= &best_num)) + .filter(|t| match t.condition { + Some(TransactionCondition::Number(ref x)) => x <= &best_num, + Some(TransactionCondition::Timestamp(ref x)) => *x <= time::get_time().sec as u64, + None => true, + }) .cloned() .collect() } @@ -135,6 +141,7 @@ impl Client { genesis_hash: genesis_hash, best_block_hash: best_block.hash, best_block_number: best_block.number, + best_block_timestamp: HeaderView::new(&self.chain.get_header(BlockId::Latest).expect("Latest hash is always in the chain")).timestamp(), ancient_block_hash: if first_block.is_some() { Some(genesis_hash) } else { None }, ancient_block_number: if first_block.is_some() { Some(0) } else { None }, first_block_hash: first_block.as_ref().map(|first| first.hash), diff --git a/ethcore/src/blockchain/best_block.rs b/ethcore/src/blockchain/best_block.rs index 4dad72fe2c9..e857a99b64f 100644 --- a/ethcore/src/blockchain/best_block.rs +++ b/ethcore/src/blockchain/best_block.rs @@ -24,6 +24,8 @@ pub struct BestBlock { pub hash: H256, /// Best block number. pub number: BlockNumber, + /// Best block timestamp. + pub timestamp: u64, /// Best block total difficulty. pub total_difficulty: U256, /// Best block uncompressed bytes diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 3876a8c724d..776c9e7526b 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -485,6 +485,7 @@ impl BlockChain { let best_block_number = bc.block_number(&best_block_hash).unwrap(); let best_block_total_difficulty = bc.block_details(&best_block_hash).unwrap().total_difficulty; let best_block_rlp = bc.block(&best_block_hash).unwrap().into_inner(); + let best_block_timestamp = BlockView::new(&best_block_rlp).header().timestamp(); let raw_first = bc.db.get(db::COL_EXTRA, b"first").unwrap().map(|v| v.to_vec()); let mut best_ancient = bc.db.get(db::COL_EXTRA, b"ancient").unwrap().map(|h| H256::from_slice(&h)); @@ -533,6 +534,7 @@ impl BlockChain { number: best_block_number, total_difficulty: best_block_total_difficulty, hash: best_block_hash, + timestamp: best_block_timestamp, block: best_block_rlp, }; @@ -585,6 +587,7 @@ impl BlockChain { number: extras.number - 1, total_difficulty: best_block_total_difficulty, hash: hash, + timestamp: BlockView::new(&best_block_rlp).header().timestamp(), block: best_block_rlp, }; // update parent extras @@ -738,6 +741,7 @@ impl BlockChain { blocks_blooms: self.prepare_block_blooms_update(bytes, &info), transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info), info: info, + timestamp: header.timestamp(), block: bytes }, is_best); @@ -786,6 +790,7 @@ impl BlockChain { blocks_blooms: self.prepare_block_blooms_update(bytes, &info), transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info), info: info, + timestamp: header.timestamp(), block: bytes, }, is_best); true @@ -850,6 +855,7 @@ impl BlockChain { blocks_blooms: self.prepare_block_blooms_update(bytes, &info), transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info), info: info.clone(), + timestamp: header.timestamp(), block: bytes, }, true); @@ -921,6 +927,7 @@ impl BlockChain { hash: update.info.hash, number: update.info.number, total_difficulty: update.info.total_difficulty, + timestamp: update.timestamp, block: update.block.to_vec(), }); }, @@ -1206,6 +1213,11 @@ impl BlockChain { self.best_block.read().number } + /// Get best block timestamp. + pub fn best_block_timestamp(&self) -> u64 { + self.best_block.read().timestamp + } + /// Get best block total difficulty. pub fn best_block_total_difficulty(&self) -> U256 { self.best_block.read().total_difficulty @@ -1293,6 +1305,7 @@ impl BlockChain { genesis_hash: self.genesis_hash(), best_block_hash: best_block.hash.clone(), best_block_number: best_block.number, + best_block_timestamp: best_block.timestamp, first_block_hash: self.first_block(), first_block_number: From::from(self.first_block_number()), ancient_block_hash: best_ancient_block.as_ref().map(|b| b.hash.clone()), diff --git a/ethcore/src/blockchain/update.rs b/ethcore/src/blockchain/update.rs index 0d7d1dbed97..914b8aa996a 100644 --- a/ethcore/src/blockchain/update.rs +++ b/ethcore/src/blockchain/update.rs @@ -9,6 +9,8 @@ use super::extras::{BlockDetails, BlockReceipts, TransactionAddress, LogGroupPos pub struct ExtrasUpdate<'a> { /// Block info. pub info: BlockInfo, + /// Block timestamp. + pub timestamp: u64, /// Current block uncompressed rlp bytes pub block: &'a [u8], /// Modified block hashes. diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index f2ad08819cf..77189c3fd54 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -1406,7 +1406,11 @@ impl BlockChainClient for Client { } fn ready_transactions(&self) -> Vec { - self.miner.ready_transactions(self.chain.read().best_block_number()) + let (number, timestamp) = { + let chain = self.chain.read(); + (chain.best_block_number(), chain.best_block_timestamp()) + }; + self.miner.ready_transactions(number, timestamp) } fn queue_consensus_message(&self, message: Bytes) { diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index af2f97ceeba..46785a6cba7 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -669,12 +669,14 @@ impl BlockChainClient for TestBlockChainClient { } fn chain_info(&self) -> BlockChainInfo { + let number = self.blocks.read().len() as BlockNumber - 1; BlockChainInfo { total_difficulty: *self.difficulty.read(), pending_total_difficulty: *self.difficulty.read(), genesis_hash: self.genesis_hash.clone(), best_block_hash: self.last_hash.read().clone(), - best_block_number: self.blocks.read().len() as BlockNumber - 1, + best_block_number: number, + best_block_timestamp: number, first_block_hash: self.first_block.read().as_ref().map(|x| x.0), first_block_number: self.first_block.read().as_ref().map(|x| x.1), ancient_block_hash: self.ancient_block.read().as_ref().map(|x| x.0), @@ -709,7 +711,8 @@ impl BlockChainClient for TestBlockChainClient { } fn ready_transactions(&self) -> Vec { - self.miner.ready_transactions(self.chain_info().best_block_number) + let info = self.chain_info(); + self.miner.ready_transactions(info.best_block_number, info.best_block_timestamp) } fn signing_network_id(&self) -> Option { None } diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 44a23993b48..06949f4bd78 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -25,7 +25,7 @@ use client::TransactionImportResult; use executive::contract_address; use block::{ClosedBlock, IsBlock, Block}; use error::*; -use transaction::{Action, UnverifiedTransaction, PendingTransaction, SignedTransaction}; +use transaction::{Action, UnverifiedTransaction, PendingTransaction, SignedTransaction, Condition as TransactionCondition}; use receipt::{Receipt, RichReceipt}; use spec::Spec; use engines::{Engine, Seal}; @@ -325,7 +325,7 @@ impl Miner { let _timer = PerfTimer::new("prepare_block"); let chain_info = chain.chain_info(); let (transactions, mut open_block, original_work_hash) = { - let transactions = {self.transaction_queue.lock().top_transactions_at(chain_info.best_block_number)}; + let transactions = {self.transaction_queue.lock().top_transactions_at(chain_info.best_block_number, chain_info.best_block_timestamp)}; let mut sealing_work = self.sealing_work.lock(); let last_work_hash = sealing_work.queue.peek_last_ref().map(|pb| pb.block().fields().header.hash()); let best_hash = chain_info.best_block_hash; @@ -597,7 +597,7 @@ impl Miner { client: &MiningBlockChainClient, transactions: Vec, default_origin: TransactionOrigin, - min_block: Option, + condition: Option, transaction_queue: &mut BanningTransactionQueue, ) -> Vec> { let accounts = self.accounts.as_ref() @@ -635,7 +635,7 @@ impl Miner { let details_provider = TransactionDetailsProvider::new(client, &self.service_transaction_action); match origin { TransactionOrigin::Local | TransactionOrigin::RetractedBlock => { - transaction_queue.add(transaction, origin, insertion_time, min_block, &details_provider) + transaction_queue.add(transaction, origin, insertion_time, condition.clone(), &details_provider) }, TransactionOrigin::External => { transaction_queue.add_with_banlist(transaction, insertion_time, &details_provider) @@ -892,7 +892,7 @@ impl MinerService for Miner { let mut transaction_queue = self.transaction_queue.lock(); // We need to re-validate transactions let import = self.add_transactions_to_queue( - chain, vec![pending.transaction.into()], TransactionOrigin::Local, pending.min_block, &mut transaction_queue + chain, vec![pending.transaction.into()], TransactionOrigin::Local, pending.condition, &mut transaction_queue ).pop().expect("one result returned per added transaction; one added => one result; qed"); match import { @@ -927,7 +927,7 @@ impl MinerService for Miner { fn pending_transactions(&self) -> Vec { let queue = self.transaction_queue.lock(); - queue.pending_transactions(BlockNumber::max_value()) + queue.pending_transactions(BlockNumber::max_value(), u64::max_value()) } fn local_transactions(&self) -> BTreeMap { @@ -942,14 +942,14 @@ impl MinerService for Miner { self.transaction_queue.lock().future_transactions() } - fn ready_transactions(&self, best_block: BlockNumber) -> Vec { + fn ready_transactions(&self, best_block: BlockNumber, best_block_timestamp: u64) -> Vec { let queue = self.transaction_queue.lock(); match self.options.pending_set { - PendingSet::AlwaysQueue => queue.pending_transactions(best_block), + PendingSet::AlwaysQueue => queue.pending_transactions(best_block, best_block_timestamp), PendingSet::SealingOrElseQueue => { self.from_pending_block( best_block, - || queue.pending_transactions(best_block), + || queue.pending_transactions(best_block, best_block_timestamp), |sealing| sealing.transactions().iter().map(|t| t.clone().into()).collect() ) }, @@ -1325,7 +1325,7 @@ mod tests { // then assert_eq!(res.unwrap(), TransactionImportResult::Current); assert_eq!(miner.pending_transactions().len(), 1); - assert_eq!(miner.ready_transactions(best_block).len(), 1); + assert_eq!(miner.ready_transactions(best_block, 0).len(), 1); assert_eq!(miner.pending_transactions_hashes(best_block).len(), 1); assert_eq!(miner.pending_receipts(best_block).len(), 1); // This method will let us know if pending block was created (before calling that method) @@ -1345,7 +1345,7 @@ mod tests { // then assert_eq!(res.unwrap(), TransactionImportResult::Current); assert_eq!(miner.pending_transactions().len(), 1); - assert_eq!(miner.ready_transactions(best_block).len(), 0); + assert_eq!(miner.ready_transactions(best_block, 0).len(), 0); assert_eq!(miner.pending_transactions_hashes(best_block).len(), 0); assert_eq!(miner.pending_receipts(best_block).len(), 0); } @@ -1364,7 +1364,7 @@ mod tests { assert_eq!(res.unwrap(), TransactionImportResult::Current); assert_eq!(miner.pending_transactions().len(), 1); assert_eq!(miner.pending_transactions_hashes(best_block).len(), 0); - assert_eq!(miner.ready_transactions(best_block).len(), 0); + assert_eq!(miner.ready_transactions(best_block, 0).len(), 0); assert_eq!(miner.pending_receipts(best_block).len(), 0); // This method will let us know if pending block was created (before calling that method) assert!(miner.prepare_work_sealing(&client)); diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index 495dc3bbaee..d88a261f368 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -154,7 +154,7 @@ pub trait MinerService : Send + Sync { fn pending_transactions(&self) -> Vec; /// Get a list of all transactions that can go into the given block. - fn ready_transactions(&self, best_block: BlockNumber) -> Vec; + fn ready_transactions(&self, best_block: BlockNumber, best_block_timestamp: u64) -> Vec; /// Get a list of all future transactions. fn future_transactions(&self) -> Vec; diff --git a/ethcore/src/miner/transaction_queue.rs b/ethcore/src/miner/transaction_queue.rs index 34305cfc95e..ef7094a90c8 100644 --- a/ethcore/src/miner/transaction_queue.rs +++ b/ethcore/src/miner/transaction_queue.rs @@ -276,17 +276,17 @@ struct VerifiedTransaction { origin: TransactionOrigin, /// Insertion time insertion_time: QueuingInstant, - /// Delay until specifid block. - min_block: Option, + /// Delay until specified condition is met. + condition: Option, } impl VerifiedTransaction { - fn new(transaction: SignedTransaction, origin: TransactionOrigin, time: QueuingInstant, min_block: Option) -> Self { + fn new(transaction: SignedTransaction, origin: TransactionOrigin, time: QueuingInstant, condition: Option) -> Self { VerifiedTransaction { transaction: transaction, origin: origin, insertion_time: time, - min_block: min_block, + condition: condition, } } @@ -666,14 +666,14 @@ impl TransactionQueue { tx: SignedTransaction, origin: TransactionOrigin, time: QueuingInstant, - min_block: Option, + condition: Option, details_provider: &TransactionDetailsProvider, ) -> Result { if origin == TransactionOrigin::Local { let hash = tx.hash(); let cloned_tx = tx.clone(); - let result = self.add_internal(tx, origin, time, min_block, details_provider); + let result = self.add_internal(tx, origin, time, condition, details_provider); match result { Ok(TransactionImportResult::Current) => { self.local_transactions.mark_pending(hash); @@ -694,7 +694,7 @@ impl TransactionQueue { } result } else { - self.add_internal(tx, origin, time, min_block, details_provider) + self.add_internal(tx, origin, time, condition, details_provider) } } @@ -704,7 +704,7 @@ impl TransactionQueue { tx: SignedTransaction, origin: TransactionOrigin, time: QueuingInstant, - min_block: Option, + condition: Option, details_provider: &TransactionDetailsProvider, ) -> Result { if origin != TransactionOrigin::Local && tx.gas_price < self.minimal_gas_price { @@ -815,7 +815,7 @@ impl TransactionQueue { } tx.check_low_s()?; // No invalid transactions beyond this point. - let vtx = VerifiedTransaction::new(tx, origin, time, min_block); + let vtx = VerifiedTransaction::new(tx, origin, time, condition); let r = self.import_tx(vtx, client_account.nonce).map_err(Error::Transaction); assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len()); r @@ -1068,11 +1068,11 @@ impl TransactionQueue { /// Returns top transactions from the queue ordered by priority. pub fn top_transactions(&self) -> Vec { - self.top_transactions_at(BlockNumber::max_value()) + self.top_transactions_at(BlockNumber::max_value(), u64::max_value()) } - fn filter_pending_transaction(&self, best_block: BlockNumber, mut f: F) + fn filter_pending_transaction(&self, best_block: BlockNumber, best_timestamp: u64, mut f: F) where F: FnMut(&VerifiedTransaction) { let mut delayed = HashSet::new(); @@ -1082,7 +1082,12 @@ impl TransactionQueue { if delayed.contains(&sender) { continue; } - if tx.min_block.unwrap_or(0) > best_block { + let delay = match tx.condition { + Some(Condition::Number(n)) => n > best_block, + Some(Condition::Timestamp(t)) => t > best_timestamp, + None => false, + }; + if delay { delayed.insert(sender); continue; } @@ -1091,16 +1096,16 @@ impl TransactionQueue { } /// Returns top transactions from the queue ordered by priority. - pub fn top_transactions_at(&self, best_block: BlockNumber) -> Vec { + pub fn top_transactions_at(&self, best_block: BlockNumber, best_timestamp: u64) -> Vec { let mut r = Vec::new(); - self.filter_pending_transaction(best_block, |tx| r.push(tx.transaction.clone())); + self.filter_pending_transaction(best_block, best_timestamp, |tx| r.push(tx.transaction.clone())); r } /// Return all ready transactions. - pub fn pending_transactions(&self, best_block: BlockNumber) -> Vec { + pub fn pending_transactions(&self, best_block: BlockNumber, best_timestamp: u64) -> Vec { let mut r = Vec::new(); - self.filter_pending_transaction(best_block, |tx| r.push(PendingTransaction::new(tx.transaction.clone(), tx.min_block))); + self.filter_pending_transaction(best_block, best_timestamp, |tx| r.push(PendingTransaction::new(tx.transaction.clone(), tx.condition.clone()))); r } @@ -1109,7 +1114,7 @@ impl TransactionQueue { self.future.by_priority .iter() .map(|t| self.by_hash.get(&t.hash).expect("All transactions in `current` and `future` are always included in `by_hash`")) - .map(|t| PendingTransaction { transaction: t.transaction.clone(), min_block: t.min_block }) + .map(|t| PendingTransaction { transaction: t.transaction.clone(), condition: t.condition.clone() }) .collect() } @@ -1382,7 +1387,7 @@ pub mod test { use super::{TransactionSet, TransactionOrder, VerifiedTransaction}; use miner::local_transactions::LocalTransactionsList; use client::TransactionImportResult; - use transaction::{SignedTransaction, Transaction, Action}; + use transaction::{SignedTransaction, Transaction, Action, Condition}; pub struct DummyTransactionDetailsProvider { account_details: AccountDetails, @@ -2178,15 +2183,15 @@ pub mod test { let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); // when - let res1 = txq.add(tx.clone(), TransactionOrigin::External, 0, Some(1), &default_tx_provider()).unwrap(); + let res1 = txq.add(tx.clone(), TransactionOrigin::External, 0, Some(Condition::Number(1)), &default_tx_provider()).unwrap(); let res2 = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); // then assert_eq!(res1, TransactionImportResult::Current); assert_eq!(res2, TransactionImportResult::Current); - let top = txq.top_transactions_at(0); + let top = txq.top_transactions_at(0, 0); assert_eq!(top.len(), 0); - let top = txq.top_transactions_at(1); + let top = txq.top_transactions_at(1, 0); assert_eq!(top.len(), 2); } diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index be703d7be58..abaf1cc3b4c 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -29,7 +29,7 @@ use spec::Spec; use views::BlockView; use util::stats::Histogram; use ethkey::{KeyPair, Secret}; -use transaction::{PendingTransaction, Transaction, Action}; +use transaction::{PendingTransaction, Transaction, Action, Condition}; use miner::MinerService; #[test] @@ -299,7 +299,7 @@ fn does_not_propagate_delayed_transactions() { action: Action::Call(Address::default()), value: 0.into(), data: Vec::new(), - }.sign(secret, None), Some(2)); + }.sign(secret, None), Some(Condition::Number(2))); let tx1 = PendingTransaction::new(Transaction { nonce: 1.into(), gas_price: 0.into(), diff --git a/ethcore/src/types/blockchain_info.rs b/ethcore/src/types/blockchain_info.rs index 48a1fde4488..1f691bc42eb 100644 --- a/ethcore/src/types/blockchain_info.rs +++ b/ethcore/src/types/blockchain_info.rs @@ -34,6 +34,8 @@ pub struct BlockChainInfo { pub best_block_hash: H256, /// Best blockchain block number. pub best_block_number: BlockNumber, + /// Best blockchain block timestamp. + pub best_block_timestamp: u64, /// Best ancient block hash. pub ancient_block_hash: Option, /// Best ancient block number. diff --git a/ethcore/src/types/transaction.rs b/ethcore/src/types/transaction.rs index 8c1f07e8598..bb117629ba5 100644 --- a/ethcore/src/types/transaction.rs +++ b/ethcore/src/types/transaction.rs @@ -52,6 +52,16 @@ impl Decodable for Action { } } +/// Transaction activation condition. +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", binary)] +pub enum Condition { + /// Valid at this block number or later. + Number(BlockNumber), + /// Valid at this unix time or later. + Timestamp(u64), +} + /// A set of information describing an externally-originating message call /// or contract creation operation. #[derive(Default, Debug, Clone, PartialEq, Eq)] @@ -448,16 +458,16 @@ impl Deref for LocalizedTransaction { pub struct PendingTransaction { /// Signed transaction data. pub transaction: SignedTransaction, - /// To be activated at this block. `None` for immediately. - pub min_block: Option, + /// To be activated at this condition. `None` for immediately. + pub condition: Option, } impl PendingTransaction { /// Create a new pending transaction from signed transaction. - pub fn new(signed: SignedTransaction, min_block: Option) -> Self { + pub fn new(signed: SignedTransaction, condition: Option) -> Self { PendingTransaction { transaction: signed, - min_block: min_block, + condition: condition, } } } @@ -466,7 +476,7 @@ impl From for PendingTransaction { fn from(t: SignedTransaction) -> Self { PendingTransaction { transaction: t, - min_block: None, + condition: None, } } } diff --git a/rpc/src/v1/helpers/dispatch.rs b/rpc/src/v1/helpers/dispatch.rs index 5ef3e8cc180..c97f9d938e6 100644 --- a/rpc/src/v1/helpers/dispatch.rs +++ b/rpc/src/v1/helpers/dispatch.rs @@ -192,7 +192,7 @@ pub fn sign_and_dispatch(client: &C, miner: &M, accounts: &AccountProvider { let network_id = client.signing_network_id(); - let min_block = filled.min_block.clone(); + let condition = filled.condition.clone(); let signed_transaction = sign_no_dispatch(client, miner, accounts, filled, password)?; let (signed_transaction, token) = match signed_transaction { @@ -201,7 +201,7 @@ pub fn sign_and_dispatch(client: &C, miner: &M, accounts: &AccountProvider }; trace!(target: "miner", "send_transaction: dispatching tx: {} for network ID {:?}", rlp::encode(&signed_transaction).to_vec().pretty(), network_id); - let pending_transaction = PendingTransaction::new(signed_transaction, min_block); + let pending_transaction = PendingTransaction::new(signed_transaction, condition.map(Into::into)); dispatch_transaction(&*client, &*miner, pending_transaction).map(|hash| { match token { Some(ref token) => WithToken::Yes(hash, token.clone()), @@ -222,7 +222,7 @@ pub fn fill_optional_fields(request: TransactionRequest, default_sender: A gas: request.gas.unwrap_or_else(|| miner.sensible_gas_limit()), value: request.value.unwrap_or_else(|| 0.into()), data: request.data.unwrap_or_else(Vec::new), - min_block: request.min_block, + condition: request.condition, } } diff --git a/rpc/src/v1/helpers/requests.rs b/rpc/src/v1/helpers/requests.rs index d7a737dc0b3..993a8c5cd80 100644 --- a/rpc/src/v1/helpers/requests.rs +++ b/rpc/src/v1/helpers/requests.rs @@ -15,6 +15,7 @@ // along with Parity. If not, see . use util::{Address, U256, Bytes}; +use v1::types::TransactionCondition; /// Transaction request coming from RPC #[derive(Debug, Clone, Default, Eq, PartialEq, Hash)] @@ -33,8 +34,8 @@ pub struct TransactionRequest { pub data: Option, /// Transaction's nonce pub nonce: Option, - /// Delay until this block if specified. - pub min_block: Option, + /// Delay until this condition is met. + pub condition: Option, } /// Transaction request coming from RPC with default values filled in. @@ -56,8 +57,8 @@ pub struct FilledTransactionRequest { pub data: Bytes, /// Transaction's nonce pub nonce: Option, - /// Delay until this block if specified. - pub min_block: Option, + /// Delay until this condition is met. + pub condition: Option, } impl From for TransactionRequest { @@ -70,7 +71,7 @@ impl From for TransactionRequest { value: Some(r.value), data: Some(r.data), nonce: r.nonce, - min_block: r.min_block, + condition: r.condition, } } } diff --git a/rpc/src/v1/helpers/signing_queue.rs b/rpc/src/v1/helpers/signing_queue.rs index 428f17b83a2..f224dcf0cfd 100644 --- a/rpc/src/v1/helpers/signing_queue.rs +++ b/rpc/src/v1/helpers/signing_queue.rs @@ -349,7 +349,7 @@ mod test { value: 10_000_000.into(), data: vec![], nonce: None, - min_block: None, + condition: None, }) } diff --git a/rpc/src/v1/impls/signer.rs b/rpc/src/v1/impls/signer.rs index 315541c1fbc..a25a5bbc1f6 100644 --- a/rpc/src/v1/impls/signer.rs +++ b/rpc/src/v1/impls/signer.rs @@ -87,8 +87,8 @@ impl SignerClient where C: MiningBlockChainClient, if let Some(gas) = modification.gas { request.gas = gas.into(); } - if let Some(ref min_block) = modification.min_block { - request.min_block = min_block.as_ref().and_then(|b| b.to_min_block_num()); + if let Some(ref condition) = modification.condition { + request.condition = condition.clone().map(Into::into); } } let result = f(&*client, &*miner, &*accounts, payload); @@ -160,7 +160,7 @@ impl Signer for SignerClient where C: MiningBlockC // Dispatch if everything is ok if sender_matches && data_matches && value_matches && nonce_matches { - let pending_transaction = PendingTransaction::new(signed_transaction, request.min_block); + let pending_transaction = PendingTransaction::new(signed_transaction, request.condition.map(Into::into)); dispatch_transaction(&*client, &*miner, pending_transaction) .map(Into::into) .map(ConfirmationResponse::SendTransaction) diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index 33c62489664..82b776d0067 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -212,7 +212,7 @@ impl MinerService for TestMinerService { self.local_transactions.lock().iter().map(|(hash, stats)| (*hash, stats.clone())).collect() } - fn ready_transactions(&self, _best_block: BlockNumber) -> Vec { + fn ready_transactions(&self, _best_block: BlockNumber, _best_timestamp: u64) -> Vec { self.pending_transactions.lock().values().cloned().map(Into::into).collect() } diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index cc7beaf508a..0c66a90fa3c 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -514,7 +514,7 @@ fn rpc_eth_pending_transaction_by_hash() { tester.miner.pending_transactions.lock().insert(H256::zero(), tx); } - let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x1","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","minBlock":null,"networkId":null,"nonce":"0x0","publicKey":"0x7ae46da747962c2ee46825839c1ef9298e3bd2e70ca2938495c3693a485ec3eaa8f196327881090ff64cf4fbb0a48485d4f83098e189ed3b7a87d5941b59f789","r":"0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","s":"0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","standardV":"0x0","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"v":"0x1b","value":"0xa"},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"condition":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x1","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","networkId":null,"nonce":"0x0","publicKey":"0x7ae46da747962c2ee46825839c1ef9298e3bd2e70ca2938495c3693a485ec3eaa8f196327881090ff64cf4fbb0a48485d4f83098e189ed3b7a87d5941b59f789","r":"0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","s":"0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","standardV":"0x0","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"v":"0x1b","value":"0xa"},"id":1}"#; let request = r#"{ "jsonrpc": "2.0", "method": "eth_getTransactionByHash", @@ -830,12 +830,11 @@ fn rpc_eth_sign_transaction() { let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() + r#""raw":"0x"# + &rlp.to_hex() + r#"","# + r#""tx":{"# + - r#""blockHash":null,"blockNumber":null,"creates":null,"# + + r#""blockHash":null,"blockNumber":null,"condition":null,"creates":null,"# + &format!("\"from\":\"0x{:?}\",", &address) + r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# + &format!("\"hash\":\"0x{:?}\",", t.hash()) + r#""input":"0x","# + - r#""minBlock":null,"# + &format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) + r#""nonce":"0x1","# + &format!("\"publicKey\":\"0x{:?}\",", t.recover_public().unwrap()) + diff --git a/rpc/src/v1/tests/mocked/signer.rs b/rpc/src/v1/tests/mocked/signer.rs index b1609cf8f60..9762a853153 100644 --- a/rpc/src/v1/tests/mocked/signer.rs +++ b/rpc/src/v1/tests/mocked/signer.rs @@ -85,7 +85,7 @@ fn should_return_list_of_items_to_confirm() { value: U256::from(1), data: vec![], nonce: None, - min_block: None, + condition: None, })).unwrap(); tester.signer.add_request(ConfirmationPayload::Signature(1.into(), vec![5].into())).unwrap(); @@ -93,7 +93,7 @@ fn should_return_list_of_items_to_confirm() { let request = r#"{"jsonrpc":"2.0","method":"signer_requestsToConfirm","params":[],"id":1}"#; let response = concat!( r#"{"jsonrpc":"2.0","result":["#, - r#"{"id":"0x1","payload":{"sendTransaction":{"data":"0x","from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","minBlock":null,"nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x1"}}},"#, + r#"{"id":"0x1","payload":{"sendTransaction":{"condition":null,"data":"0x","from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x1"}}},"#, r#"{"id":"0x2","payload":{"sign":{"address":"0x0000000000000000000000000000000000000001","data":"0x05"}}}"#, r#"],"id":1}"# ); @@ -116,7 +116,7 @@ fn should_reject_transaction_from_queue_without_dispatching() { value: U256::from(1), data: vec![], nonce: None, - min_block: None, + condition: None, })).unwrap(); assert_eq!(tester.signer.requests().len(), 1); @@ -143,7 +143,7 @@ fn should_not_remove_transaction_if_password_is_invalid() { value: U256::from(1), data: vec![], nonce: None, - min_block: None, + condition: None, })).unwrap(); assert_eq!(tester.signer.requests().len(), 1); @@ -187,7 +187,7 @@ fn should_confirm_transaction_and_dispatch() { value: U256::from(1), data: vec![], nonce: None, - min_block: None, + condition: None, })).unwrap(); let t = Transaction { @@ -233,7 +233,7 @@ fn should_alter_the_sender_and_nonce() { value: U256::from(1), data: vec![], nonce: Some(10.into()), - min_block: None, + condition: None, })).unwrap(); let t = Transaction { @@ -283,7 +283,7 @@ fn should_confirm_transaction_with_token() { value: U256::from(1), data: vec![], nonce: None, - min_block: None, + condition: None, })).unwrap(); let t = Transaction { @@ -332,7 +332,7 @@ fn should_confirm_transaction_with_rlp() { value: U256::from(1), data: vec![], nonce: None, - min_block: None, + condition: None, })).unwrap(); let t = Transaction { @@ -380,7 +380,7 @@ fn should_return_error_when_sender_does_not_match() { value: U256::from(1), data: vec![], nonce: None, - min_block: None, + condition: None, })).unwrap(); let t = Transaction { diff --git a/rpc/src/v1/tests/mocked/signing.rs b/rpc/src/v1/tests/mocked/signing.rs index 7413acc215d..0f77325dd10 100644 --- a/rpc/src/v1/tests/mocked/signing.rs +++ b/rpc/src/v1/tests/mocked/signing.rs @@ -277,12 +277,11 @@ fn should_add_sign_transaction_to_the_queue() { let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() + r#""raw":"0x"# + &rlp.to_hex() + r#"","# + r#""tx":{"# + - r#""blockHash":null,"blockNumber":null,"creates":null,"# + + r#""blockHash":null,"blockNumber":null,"condition":null,"creates":null,"# + &format!("\"from\":\"0x{:?}\",", &address) + r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# + &format!("\"hash\":\"0x{:?}\",", t.hash()) + r#""input":"0x","# + - r#""minBlock":null,"# + &format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) + r#""nonce":"0x1","# + &format!("\"publicKey\":\"0x{:?}\",", t.public_key()) + diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index b020ac6e165..d9848a0ac97 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -139,7 +139,7 @@ mod tests { fn test_serialize_block_transactions() { let t = BlockTransactions::Full(vec![Transaction::default()]); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0","minBlock":null}]"#); + assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0","condition":null}]"#); let t = BlockTransactions::Hashes(vec![H256::default().into()]); let serialized = serde_json::to_string(&t).unwrap(); diff --git a/rpc/src/v1/types/confirmations.rs b/rpc/src/v1/types/confirmations.rs index 0c36fdff9b2..63d4819f3e9 100644 --- a/rpc/src/v1/types/confirmations.rs +++ b/rpc/src/v1/types/confirmations.rs @@ -21,7 +21,7 @@ use serde::{Serialize, Serializer}; use util::log::Colour; use util::bytes::ToPretty; -use v1::types::{U256, TransactionRequest, RichRawTransaction, H160, H256, H520, Bytes, BlockNumber}; +use v1::types::{U256, TransactionRequest, RichRawTransaction, H160, H256, H520, Bytes, TransactionCondition}; use v1::helpers; /// Confirmation waiting in a queue @@ -196,9 +196,8 @@ pub struct TransactionModification { pub gas_price: Option, /// Modified gas pub gas: Option, - /// Modified min block - #[serde(rename="minBlock")] - pub min_block: Option>, + /// Modified transaction condition. + pub condition: Option>, } /// Represents two possible return values. @@ -240,7 +239,7 @@ impl Serialize for Either where mod tests { use std::str::FromStr; use serde_json; - use v1::types::{U256, H256, BlockNumber}; + use v1::types::{U256, H256, TransactionCondition}; use v1::helpers; use super::*; @@ -274,13 +273,13 @@ mod tests { value: 100_000.into(), data: vec![1, 2, 3], nonce: Some(1.into()), - min_block: None, + condition: None, }), }; // when let res = serde_json::to_string(&ConfirmationRequest::from(request)); - let expected = r#"{"id":"0xf","payload":{"sendTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1","minBlock":null}}}"#; + let expected = r#"{"id":"0xf","payload":{"sendTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1","condition":null}}}"#; // then assert_eq!(res.unwrap(), expected.to_owned()); @@ -300,13 +299,13 @@ mod tests { value: 100_000.into(), data: vec![1, 2, 3], nonce: Some(1.into()), - min_block: None, + condition: None, }), }; // when let res = serde_json::to_string(&ConfirmationRequest::from(request)); - let expected = r#"{"id":"0xf","payload":{"signTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1","minBlock":null}}}"#; + let expected = r#"{"id":"0xf","payload":{"signTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1","condition":null}}}"#; // then assert_eq!(res.unwrap(), expected.to_owned()); @@ -336,7 +335,7 @@ mod tests { let s1 = r#"{ "sender": "0x000000000000000000000000000000000000000a", "gasPrice":"0xba43b7400", - "minBlock":"0x42" + "condition": { "block": 66 } }"#; let s2 = r#"{"gas": "0x1233"}"#; let s3 = r#"{}"#; @@ -351,19 +350,19 @@ mod tests { sender: Some(10.into()), gas_price: Some(U256::from_str("0ba43b7400").unwrap()), gas: None, - min_block: Some(Some(BlockNumber::Num(0x42))), + condition: Some(Some(TransactionCondition::Number(0x42))), }); assert_eq!(res2, TransactionModification { sender: None, gas_price: None, gas: Some(U256::from_str("1233").unwrap()), - min_block: None, + condition: None, }); assert_eq!(res3, TransactionModification { sender: None, gas_price: None, gas: None, - min_block: None, + condition: None, }); } diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index 3f84bffafd7..a823a010402 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -27,6 +27,7 @@ mod log; mod sync; mod transaction; mod transaction_request; +mod transaction_condition; mod receipt; mod rpc_settings; mod trace; @@ -55,6 +56,7 @@ pub use self::sync::{ }; pub use self::transaction::{Transaction, RichRawTransaction, LocalTransactionStatus}; pub use self::transaction_request::TransactionRequest; +pub use self::transaction_condition::TransactionCondition; pub use self::receipt::Receipt; pub use self::rpc_settings::RpcSettings; pub use self::trace::{LocalizedTrace, TraceResults}; diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index f47b236672f..106392fe1d6 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -19,7 +19,7 @@ use ethcore::miner; use ethcore::contract_address; use ethcore::transaction::{LocalizedTransaction, Action, PendingTransaction, SignedTransaction}; use v1::helpers::errors; -use v1::types::{Bytes, H160, H256, U256, H512, BlockNumber}; +use v1::types::{Bytes, H160, H256, U256, H512, TransactionCondition}; /// Transaction #[derive(Debug, Default, Clone, PartialEq, Serialize)] @@ -70,8 +70,7 @@ pub struct Transaction { /// The S field of the signature. pub s: U256, /// Transaction activates at specified block. - #[serde(rename="minBlock")] - pub min_block: Option, + pub condition: Option, } /// Local Transaction Status @@ -190,7 +189,7 @@ impl From for Transaction { v: t.original_v().into(), r: signature.r().into(), s: signature.s().into(), - min_block: None, + condition: None, } } } @@ -224,7 +223,7 @@ impl From for Transaction { v: t.original_v().into(), r: signature.r().into(), s: signature.s().into(), - min_block: None, + condition: None, } } } @@ -232,7 +231,7 @@ impl From for Transaction { impl From for Transaction { fn from(t: PendingTransaction) -> Transaction { let mut r = Transaction::from(t.transaction); - r.min_block = t.min_block.map(|b| BlockNumber::Num(b)); + r.condition = t.condition.map(|b| b.into()); r } } @@ -261,7 +260,7 @@ mod tests { fn test_transaction_serialize() { let t = Transaction::default(); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0","minBlock":null}"#); + assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0","condition":null}"#); } #[test] diff --git a/rpc/src/v1/types/transaction_condition.rs b/rpc/src/v1/types/transaction_condition.rs new file mode 100644 index 00000000000..2f530f6861c --- /dev/null +++ b/rpc/src/v1/types/transaction_condition.rs @@ -0,0 +1,67 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use ethcore; + +/// Represents condition on minimum block number or block timestamp. +#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] +pub enum TransactionCondition { + /// Valid at this minimum block number. + #[serde(rename="block")] + Number(u64), + /// Valid at given unix time. + #[serde(rename="time")] + Timestamp(u64), +} + +impl Into for TransactionCondition { + fn into(self) -> ethcore::transaction::Condition { + match self { + TransactionCondition::Number(n) => ethcore::transaction::Condition::Number(n), + TransactionCondition::Timestamp(n) => ethcore::transaction::Condition::Timestamp(n), + } + } +} + +impl From for TransactionCondition { + fn from(condition: ethcore::transaction::Condition) -> Self { + match condition { + ethcore::transaction::Condition::Number(n) => TransactionCondition::Number(n), + ethcore::transaction::Condition::Timestamp(n) => TransactionCondition::Timestamp(n), + } + } +} + +#[cfg(test)] +mod tests { + use ethcore; + use super::*; + use serde_json; + + #[test] + fn condition_deserialization() { + let s = r#"[{ "block": 51 }, { "time": 10 }]"#; + let deserialized: Vec = serde_json::from_str(s).unwrap(); + assert_eq!(deserialized, vec![TransactionCondition::Number(51), TransactionCondition::Timestamp(10)]) + } + + #[test] + fn condition_into() { + assert_eq!(ethcore::transaction::Condition::Number(100), TransactionCondition::Number(100).into()); + assert_eq!(ethcore::transaction::Condition::Timestamp(100), TransactionCondition::Timestamp(100).into()); + } +} + diff --git a/rpc/src/v1/types/transaction_request.rs b/rpc/src/v1/types/transaction_request.rs index 9434983fa47..5839a0de852 100644 --- a/rpc/src/v1/types/transaction_request.rs +++ b/rpc/src/v1/types/transaction_request.rs @@ -16,7 +16,7 @@ //! `TransactionRequest` type -use v1::types::{Bytes, H160, U256, BlockNumber}; +use v1::types::{Bytes, H160, U256, TransactionCondition}; use v1::helpers; use util::log::Colour; @@ -41,9 +41,8 @@ pub struct TransactionRequest { pub data: Option, /// Transaction's nonce pub nonce: Option, - /// Delay until this block if specified. - #[serde(rename="minBlock")] - pub min_block: Option, + /// Delay until this block condition. + pub condition: Option, } pub fn format_ether(i: U256) -> String { @@ -93,7 +92,7 @@ impl From for TransactionRequest { value: r.value.map(Into::into), data: r.data.map(Into::into), nonce: r.nonce.map(Into::into), - min_block: r.min_block.map(|b| BlockNumber::Num(b)), + condition: r.condition.map(Into::into), } } } @@ -108,7 +107,7 @@ impl From for TransactionRequest { value: Some(r.value.into()), data: Some(r.data.into()), nonce: r.nonce.map(Into::into), - min_block: r.min_block.map(|b| BlockNumber::Num(b)), + condition: r.condition.map(Into::into), } } } @@ -123,7 +122,7 @@ impl Into for TransactionRequest { value: self.value.map(Into::into), data: self.data.map(Into::into), nonce: self.nonce.map(Into::into), - min_block: self.min_block.and_then(|b| b.to_min_block_num()), + condition: self.condition.map(Into::into), } } } @@ -134,7 +133,7 @@ mod tests { use std::str::FromStr; use rustc_serialize::hex::FromHex; use serde_json; - use v1::types::{U256, H160, BlockNumber}; + use v1::types::{U256, H160, TransactionCondition}; use super::*; #[test] @@ -147,7 +146,7 @@ mod tests { "value":"0x3", "data":"0x123456", "nonce":"0x4", - "minBlock":"0x13" + "condition": { "block": 19 } }"#; let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); @@ -159,7 +158,7 @@ mod tests { value: Some(U256::from(3)), data: Some(vec![0x12, 0x34, 0x56].into()), nonce: Some(U256::from(4)), - min_block: Some(BlockNumber::Num(0x13)), + condition: Some(TransactionCondition::Number(0x13)), }); } @@ -183,7 +182,7 @@ mod tests { value: Some(U256::from_str("9184e72a").unwrap()), data: Some("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap().into()), nonce: None, - min_block: None, + condition: None, }); } @@ -200,7 +199,7 @@ mod tests { value: None, data: None, nonce: None, - min_block: None, + condition: None, }); } @@ -224,7 +223,7 @@ mod tests { value: None, data: Some(vec![0x85, 0x95, 0xba, 0xb1].into()), nonce: None, - min_block: None, + condition: None, }); } diff --git a/rpc_client/src/signer_client.rs b/rpc_client/src/signer_client.rs index bff6be275c2..956a28208cd 100644 --- a/rpc_client/src/signer_client.rs +++ b/rpc_client/src/signer_client.rs @@ -1,5 +1,5 @@ use client::{Rpc, RpcError}; -use rpc::v1::types::{ConfirmationRequest, TransactionModification, U256, BlockNumber}; +use rpc::v1::types::{ConfirmationRequest, TransactionModification, U256, TransactionCondition}; use serde_json::{Value as JsonValue, to_value}; use std::path::PathBuf; use futures::{BoxFuture, Canceled}; @@ -22,13 +22,13 @@ impl SignerRpc { id: U256, new_gas: Option, new_gas_price: Option, - new_min_block: Option>, + new_condition: Option>, pwd: &str ) -> BoxFuture, Canceled> { self.rpc.request("signer_confirmRequest", vec![ to_value(&format!("{:#x}", id)), - to_value(&TransactionModification { sender: None, gas_price: new_gas_price, gas: new_gas, min_block: new_min_block }), + to_value(&TransactionModification { sender: None, gas_price: new_gas_price, gas: new_gas, condition: new_condition }), to_value(&pwd), ]) }