Transaction timestamp condtiion (#4427)
This commit is contained in:
parent
e9b1921950
commit
f187b15a43
@ -197,6 +197,7 @@ impl HeaderChain {
|
|||||||
pub fn get_header(&self, id: BlockId) -> Option<Bytes> {
|
pub fn get_header(&self, id: BlockId) -> Option<Bytes> {
|
||||||
match id {
|
match id {
|
||||||
BlockId::Earliest | BlockId::Number(0) => Some(self.genesis_header.clone()),
|
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::Hash(hash) => self.headers.read().get(&hash).map(|x| x.to_vec()),
|
||||||
BlockId::Number(num) => {
|
BlockId::Number(num) => {
|
||||||
if self.best_block.read().number < num { return None }
|
if self.best_block.read().number < num { return None }
|
||||||
|
@ -21,8 +21,9 @@ use ethcore::block_status::BlockStatus;
|
|||||||
use ethcore::client::ClientReport;
|
use ethcore::client::ClientReport;
|
||||||
use ethcore::ids::BlockId;
|
use ethcore::ids::BlockId;
|
||||||
use ethcore::header::Header;
|
use ethcore::header::Header;
|
||||||
|
use ethcore::views::HeaderView;
|
||||||
use ethcore::verification::queue::{self, HeaderQueue};
|
use ethcore::verification::queue::{self, HeaderQueue};
|
||||||
use ethcore::transaction::PendingTransaction;
|
use ethcore::transaction::{PendingTransaction, Condition as TransactionCondition};
|
||||||
use ethcore::blockchain_info::BlockChainInfo;
|
use ethcore::blockchain_info::BlockChainInfo;
|
||||||
use ethcore::spec::Spec;
|
use ethcore::spec::Spec;
|
||||||
use ethcore::service::ClientIoMessage;
|
use ethcore::service::ClientIoMessage;
|
||||||
@ -34,6 +35,7 @@ use util::{Bytes, Mutex, RwLock};
|
|||||||
|
|
||||||
use provider::Provider;
|
use provider::Provider;
|
||||||
use request;
|
use request;
|
||||||
|
use time;
|
||||||
|
|
||||||
use self::header_chain::HeaderChain;
|
use self::header_chain::HeaderChain;
|
||||||
|
|
||||||
@ -109,7 +111,11 @@ impl Client {
|
|||||||
let best_num = self.chain.best_block().number;
|
let best_num = self.chain.best_block().number;
|
||||||
self.tx_pool.lock()
|
self.tx_pool.lock()
|
||||||
.values()
|
.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()
|
.cloned()
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
@ -134,6 +140,7 @@ impl Client {
|
|||||||
genesis_hash: genesis_hash,
|
genesis_hash: genesis_hash,
|
||||||
best_block_hash: best_block.hash,
|
best_block_hash: best_block.hash,
|
||||||
best_block_number: best_block.number,
|
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_hash: if first_block.is_some() { Some(genesis_hash) } else { None },
|
||||||
ancient_block_number: if first_block.is_some() { Some(0) } 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),
|
first_block_hash: first_block.as_ref().map(|first| first.hash),
|
||||||
|
@ -24,6 +24,8 @@ pub struct BestBlock {
|
|||||||
pub hash: H256,
|
pub hash: H256,
|
||||||
/// Best block number.
|
/// Best block number.
|
||||||
pub number: BlockNumber,
|
pub number: BlockNumber,
|
||||||
|
/// Best block timestamp.
|
||||||
|
pub timestamp: u64,
|
||||||
/// Best block total difficulty.
|
/// Best block total difficulty.
|
||||||
pub total_difficulty: U256,
|
pub total_difficulty: U256,
|
||||||
/// Best block uncompressed bytes
|
/// Best block uncompressed bytes
|
||||||
|
@ -490,6 +490,7 @@ impl BlockChain {
|
|||||||
let best_block_number = bc.block_number(&best_block_hash).unwrap();
|
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_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_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 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));
|
let mut best_ancient = bc.db.get(db::COL_EXTRA, b"ancient").unwrap().map(|h| H256::from_slice(&h));
|
||||||
@ -538,6 +539,7 @@ impl BlockChain {
|
|||||||
number: best_block_number,
|
number: best_block_number,
|
||||||
total_difficulty: best_block_total_difficulty,
|
total_difficulty: best_block_total_difficulty,
|
||||||
hash: best_block_hash,
|
hash: best_block_hash,
|
||||||
|
timestamp: best_block_timestamp,
|
||||||
block: best_block_rlp,
|
block: best_block_rlp,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -590,6 +592,7 @@ impl BlockChain {
|
|||||||
number: extras.number - 1,
|
number: extras.number - 1,
|
||||||
total_difficulty: best_block_total_difficulty,
|
total_difficulty: best_block_total_difficulty,
|
||||||
hash: hash,
|
hash: hash,
|
||||||
|
timestamp: BlockView::new(&best_block_rlp).header().timestamp(),
|
||||||
block: best_block_rlp,
|
block: best_block_rlp,
|
||||||
};
|
};
|
||||||
// update parent extras
|
// update parent extras
|
||||||
@ -743,6 +746,7 @@ impl BlockChain {
|
|||||||
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
|
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
|
||||||
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
|
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
|
||||||
info: info,
|
info: info,
|
||||||
|
timestamp: header.timestamp(),
|
||||||
block: bytes
|
block: bytes
|
||||||
}, is_best);
|
}, is_best);
|
||||||
|
|
||||||
@ -791,6 +795,7 @@ impl BlockChain {
|
|||||||
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
|
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
|
||||||
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
|
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
|
||||||
info: info,
|
info: info,
|
||||||
|
timestamp: header.timestamp(),
|
||||||
block: bytes,
|
block: bytes,
|
||||||
}, is_best);
|
}, is_best);
|
||||||
true
|
true
|
||||||
@ -855,6 +860,7 @@ impl BlockChain {
|
|||||||
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
|
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
|
||||||
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
|
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
|
||||||
info: info.clone(),
|
info: info.clone(),
|
||||||
|
timestamp: header.timestamp(),
|
||||||
block: bytes,
|
block: bytes,
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
@ -926,6 +932,7 @@ impl BlockChain {
|
|||||||
hash: update.info.hash,
|
hash: update.info.hash,
|
||||||
number: update.info.number,
|
number: update.info.number,
|
||||||
total_difficulty: update.info.total_difficulty,
|
total_difficulty: update.info.total_difficulty,
|
||||||
|
timestamp: update.timestamp,
|
||||||
block: update.block.to_vec(),
|
block: update.block.to_vec(),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -1211,6 +1218,11 @@ impl BlockChain {
|
|||||||
self.best_block.read().number
|
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.
|
/// Get best block total difficulty.
|
||||||
pub fn best_block_total_difficulty(&self) -> U256 {
|
pub fn best_block_total_difficulty(&self) -> U256 {
|
||||||
self.best_block.read().total_difficulty
|
self.best_block.read().total_difficulty
|
||||||
@ -1298,6 +1310,7 @@ impl BlockChain {
|
|||||||
genesis_hash: self.genesis_hash(),
|
genesis_hash: self.genesis_hash(),
|
||||||
best_block_hash: best_block.hash.clone(),
|
best_block_hash: best_block.hash.clone(),
|
||||||
best_block_number: best_block.number,
|
best_block_number: best_block.number,
|
||||||
|
best_block_timestamp: best_block.timestamp,
|
||||||
first_block_hash: self.first_block(),
|
first_block_hash: self.first_block(),
|
||||||
first_block_number: From::from(self.first_block_number()),
|
first_block_number: From::from(self.first_block_number()),
|
||||||
ancient_block_hash: best_ancient_block.as_ref().map(|b| b.hash.clone()),
|
ancient_block_hash: best_ancient_block.as_ref().map(|b| b.hash.clone()),
|
||||||
|
@ -9,6 +9,8 @@ use super::extras::{BlockDetails, BlockReceipts, TransactionAddress, LogGroupPos
|
|||||||
pub struct ExtrasUpdate<'a> {
|
pub struct ExtrasUpdate<'a> {
|
||||||
/// Block info.
|
/// Block info.
|
||||||
pub info: BlockInfo,
|
pub info: BlockInfo,
|
||||||
|
/// Block timestamp.
|
||||||
|
pub timestamp: u64,
|
||||||
/// Current block uncompressed rlp bytes
|
/// Current block uncompressed rlp bytes
|
||||||
pub block: &'a [u8],
|
pub block: &'a [u8],
|
||||||
/// Modified block hashes.
|
/// Modified block hashes.
|
||||||
|
@ -1382,7 +1382,11 @@ impl BlockChainClient for Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn ready_transactions(&self) -> Vec<PendingTransaction> {
|
fn ready_transactions(&self) -> Vec<PendingTransaction> {
|
||||||
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) {
|
fn queue_consensus_message(&self, message: Bytes) {
|
||||||
|
@ -661,12 +661,14 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn chain_info(&self) -> BlockChainInfo {
|
fn chain_info(&self) -> BlockChainInfo {
|
||||||
|
let number = self.blocks.read().len() as BlockNumber - 1;
|
||||||
BlockChainInfo {
|
BlockChainInfo {
|
||||||
total_difficulty: *self.difficulty.read(),
|
total_difficulty: *self.difficulty.read(),
|
||||||
pending_total_difficulty: *self.difficulty.read(),
|
pending_total_difficulty: *self.difficulty.read(),
|
||||||
genesis_hash: self.genesis_hash.clone(),
|
genesis_hash: self.genesis_hash.clone(),
|
||||||
best_block_hash: self.last_hash.read().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_hash: self.first_block.read().as_ref().map(|x| x.0),
|
||||||
first_block_number: self.first_block.read().as_ref().map(|x| x.1),
|
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),
|
ancient_block_hash: self.ancient_block.read().as_ref().map(|x| x.0),
|
||||||
@ -701,7 +703,8 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn ready_transactions(&self) -> Vec<PendingTransaction> {
|
fn ready_transactions(&self) -> Vec<PendingTransaction> {
|
||||||
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<u64> { None }
|
fn signing_network_id(&self) -> Option<u64> { None }
|
||||||
|
@ -26,7 +26,7 @@ use client::TransactionImportResult;
|
|||||||
use executive::contract_address;
|
use executive::contract_address;
|
||||||
use block::{ClosedBlock, IsBlock, Block};
|
use block::{ClosedBlock, IsBlock, Block};
|
||||||
use error::*;
|
use error::*;
|
||||||
use transaction::{Action, SignedTransaction, PendingTransaction};
|
use transaction::{Action, SignedTransaction, PendingTransaction, Condition as TransactionCondition};
|
||||||
use receipt::{Receipt, RichReceipt};
|
use receipt::{Receipt, RichReceipt};
|
||||||
use spec::Spec;
|
use spec::Spec;
|
||||||
use engines::{Engine, Seal};
|
use engines::{Engine, Seal};
|
||||||
@ -318,7 +318,7 @@ impl Miner {
|
|||||||
let _timer = PerfTimer::new("prepare_block");
|
let _timer = PerfTimer::new("prepare_block");
|
||||||
let chain_info = chain.chain_info();
|
let chain_info = chain.chain_info();
|
||||||
let (transactions, mut open_block, original_work_hash) = {
|
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 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 last_work_hash = sealing_work.queue.peek_last_ref().map(|pb| pb.block().fields().header.hash());
|
||||||
let best_hash = chain_info.best_block_hash;
|
let best_hash = chain_info.best_block_hash;
|
||||||
@ -586,7 +586,7 @@ impl Miner {
|
|||||||
client: &MiningBlockChainClient,
|
client: &MiningBlockChainClient,
|
||||||
transactions: Vec<SignedTransaction>,
|
transactions: Vec<SignedTransaction>,
|
||||||
default_origin: TransactionOrigin,
|
default_origin: TransactionOrigin,
|
||||||
min_block: Option<BlockNumber>,
|
condition: Option<TransactionCondition>,
|
||||||
transaction_queue: &mut BanningTransactionQueue,
|
transaction_queue: &mut BanningTransactionQueue,
|
||||||
) -> Vec<Result<TransactionImportResult, Error>> {
|
) -> Vec<Result<TransactionImportResult, Error>> {
|
||||||
let accounts = self.accounts.as_ref()
|
let accounts = self.accounts.as_ref()
|
||||||
@ -615,7 +615,7 @@ impl Miner {
|
|||||||
let details_provider = TransactionDetailsProvider::new(client, &self.service_transaction_action);
|
let details_provider = TransactionDetailsProvider::new(client, &self.service_transaction_action);
|
||||||
match origin {
|
match origin {
|
||||||
TransactionOrigin::Local | TransactionOrigin::RetractedBlock => {
|
TransactionOrigin::Local | TransactionOrigin::RetractedBlock => {
|
||||||
transaction_queue.add(tx, origin, insertion_time, min_block, &details_provider)
|
transaction_queue.add(tx, origin, insertion_time, condition.clone(), &details_provider)
|
||||||
},
|
},
|
||||||
TransactionOrigin::External => {
|
TransactionOrigin::External => {
|
||||||
transaction_queue.add_with_banlist(tx, insertion_time, &details_provider)
|
transaction_queue.add_with_banlist(tx, insertion_time, &details_provider)
|
||||||
@ -872,7 +872,7 @@ impl MinerService for Miner {
|
|||||||
// Be sure to release the lock before we call prepare_work_sealing
|
// Be sure to release the lock before we call prepare_work_sealing
|
||||||
let mut transaction_queue = self.transaction_queue.lock();
|
let mut transaction_queue = self.transaction_queue.lock();
|
||||||
let import = self.add_transactions_to_queue(
|
let import = self.add_transactions_to_queue(
|
||||||
chain, vec![pending.transaction], TransactionOrigin::Local, pending.min_block, &mut transaction_queue
|
chain, vec![pending.transaction], TransactionOrigin::Local, pending.condition, &mut transaction_queue
|
||||||
).pop().expect("one result returned per added transaction; one added => one result; qed");
|
).pop().expect("one result returned per added transaction; one added => one result; qed");
|
||||||
|
|
||||||
match import {
|
match import {
|
||||||
@ -907,7 +907,7 @@ impl MinerService for Miner {
|
|||||||
|
|
||||||
fn pending_transactions(&self) -> Vec<PendingTransaction> {
|
fn pending_transactions(&self) -> Vec<PendingTransaction> {
|
||||||
let queue = self.transaction_queue.lock();
|
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<H256, LocalTransactionStatus> {
|
fn local_transactions(&self) -> BTreeMap<H256, LocalTransactionStatus> {
|
||||||
@ -922,14 +922,14 @@ impl MinerService for Miner {
|
|||||||
self.transaction_queue.lock().future_transactions()
|
self.transaction_queue.lock().future_transactions()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ready_transactions(&self, best_block: BlockNumber) -> Vec<PendingTransaction> {
|
fn ready_transactions(&self, best_block: BlockNumber, best_block_timestamp: u64) -> Vec<PendingTransaction> {
|
||||||
let queue = self.transaction_queue.lock();
|
let queue = self.transaction_queue.lock();
|
||||||
match self.options.pending_set {
|
match self.options.pending_set {
|
||||||
PendingSet::AlwaysQueue => queue.pending_transactions(best_block),
|
PendingSet::AlwaysQueue => queue.pending_transactions(best_block, best_block_timestamp),
|
||||||
PendingSet::SealingOrElseQueue => {
|
PendingSet::SealingOrElseQueue => {
|
||||||
self.from_pending_block(
|
self.from_pending_block(
|
||||||
best_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()
|
|sealing| sealing.transactions().iter().map(|t| t.clone().into()).collect()
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -1312,7 +1312,7 @@ mod tests {
|
|||||||
// then
|
// then
|
||||||
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||||
assert_eq!(miner.pending_transactions().len(), 1);
|
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_transactions_hashes(best_block).len(), 1);
|
||||||
assert_eq!(miner.pending_receipts(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)
|
// This method will let us know if pending block was created (before calling that method)
|
||||||
@ -1332,7 +1332,7 @@ mod tests {
|
|||||||
// then
|
// then
|
||||||
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||||
assert_eq!(miner.pending_transactions().len(), 1);
|
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_transactions_hashes(best_block).len(), 0);
|
||||||
assert_eq!(miner.pending_receipts(best_block).len(), 0);
|
assert_eq!(miner.pending_receipts(best_block).len(), 0);
|
||||||
}
|
}
|
||||||
@ -1351,7 +1351,7 @@ mod tests {
|
|||||||
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||||
assert_eq!(miner.pending_transactions().len(), 1);
|
assert_eq!(miner.pending_transactions().len(), 1);
|
||||||
assert_eq!(miner.pending_transactions_hashes(best_block).len(), 0);
|
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);
|
assert_eq!(miner.pending_receipts(best_block).len(), 0);
|
||||||
// This method will let us know if pending block was created (before calling that method)
|
// This method will let us know if pending block was created (before calling that method)
|
||||||
assert!(miner.prepare_work_sealing(&client));
|
assert!(miner.prepare_work_sealing(&client));
|
||||||
|
@ -150,7 +150,7 @@ pub trait MinerService : Send + Sync {
|
|||||||
fn pending_transactions(&self) -> Vec<PendingTransaction>;
|
fn pending_transactions(&self) -> Vec<PendingTransaction>;
|
||||||
|
|
||||||
/// Get a list of all transactions that can go into the given block.
|
/// Get a list of all transactions that can go into the given block.
|
||||||
fn ready_transactions(&self, best_block: BlockNumber) -> Vec<PendingTransaction>;
|
fn ready_transactions(&self, best_block: BlockNumber, best_block_timestamp: u64) -> Vec<PendingTransaction>;
|
||||||
|
|
||||||
/// Get a list of all future transactions.
|
/// Get a list of all future transactions.
|
||||||
fn future_transactions(&self) -> Vec<PendingTransaction>;
|
fn future_transactions(&self) -> Vec<PendingTransaction>;
|
||||||
|
@ -276,18 +276,18 @@ struct VerifiedTransaction {
|
|||||||
origin: TransactionOrigin,
|
origin: TransactionOrigin,
|
||||||
/// Insertion time
|
/// Insertion time
|
||||||
insertion_time: QueuingInstant,
|
insertion_time: QueuingInstant,
|
||||||
/// Delay until specifid block.
|
/// Delay until specified condition is met.
|
||||||
min_block: Option<BlockNumber>,
|
condition: Option<Condition>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VerifiedTransaction {
|
impl VerifiedTransaction {
|
||||||
fn new(transaction: SignedTransaction, origin: TransactionOrigin, time: QueuingInstant, min_block: Option<BlockNumber>) -> Result<Self, Error> {
|
fn new(transaction: SignedTransaction, origin: TransactionOrigin, time: QueuingInstant, condition: Option<Condition>) -> Result<Self, Error> {
|
||||||
transaction.sender()?;
|
transaction.sender()?;
|
||||||
Ok(VerifiedTransaction {
|
Ok(VerifiedTransaction {
|
||||||
transaction: transaction,
|
transaction: transaction,
|
||||||
origin: origin,
|
origin: origin,
|
||||||
insertion_time: time,
|
insertion_time: time,
|
||||||
min_block: min_block,
|
condition: condition,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -667,14 +667,14 @@ impl TransactionQueue {
|
|||||||
tx: SignedTransaction,
|
tx: SignedTransaction,
|
||||||
origin: TransactionOrigin,
|
origin: TransactionOrigin,
|
||||||
time: QueuingInstant,
|
time: QueuingInstant,
|
||||||
min_block: Option<BlockNumber>,
|
condition: Option<Condition>,
|
||||||
details_provider: &TransactionDetailsProvider,
|
details_provider: &TransactionDetailsProvider,
|
||||||
) -> Result<TransactionImportResult, Error> {
|
) -> Result<TransactionImportResult, Error> {
|
||||||
if origin == TransactionOrigin::Local {
|
if origin == TransactionOrigin::Local {
|
||||||
let hash = tx.hash();
|
let hash = tx.hash();
|
||||||
let cloned_tx = tx.clone();
|
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 {
|
match result {
|
||||||
Ok(TransactionImportResult::Current) => {
|
Ok(TransactionImportResult::Current) => {
|
||||||
self.local_transactions.mark_pending(hash);
|
self.local_transactions.mark_pending(hash);
|
||||||
@ -695,7 +695,7 @@ impl TransactionQueue {
|
|||||||
}
|
}
|
||||||
result
|
result
|
||||||
} else {
|
} else {
|
||||||
self.add_internal(tx, origin, time, min_block, details_provider)
|
self.add_internal(tx, origin, time, condition, details_provider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -705,7 +705,7 @@ impl TransactionQueue {
|
|||||||
tx: SignedTransaction,
|
tx: SignedTransaction,
|
||||||
origin: TransactionOrigin,
|
origin: TransactionOrigin,
|
||||||
time: QueuingInstant,
|
time: QueuingInstant,
|
||||||
min_block: Option<BlockNumber>,
|
condition: Option<Condition>,
|
||||||
details_provider: &TransactionDetailsProvider,
|
details_provider: &TransactionDetailsProvider,
|
||||||
) -> Result<TransactionImportResult, Error> {
|
) -> Result<TransactionImportResult, Error> {
|
||||||
if origin != TransactionOrigin::Local && tx.gas_price < self.minimal_gas_price {
|
if origin != TransactionOrigin::Local && tx.gas_price < self.minimal_gas_price {
|
||||||
@ -804,7 +804,7 @@ impl TransactionQueue {
|
|||||||
|
|
||||||
let client_account = details_provider.fetch_account(&tx.sender()?);
|
let client_account = details_provider.fetch_account(&tx.sender()?);
|
||||||
let cost = tx.value + tx.gas_price * tx.gas;
|
let cost = tx.value + tx.gas_price * tx.gas;
|
||||||
let vtx = VerifiedTransaction::new(tx, origin, time, min_block)?;
|
let vtx = VerifiedTransaction::new(tx, origin, time, condition)?;
|
||||||
if client_account.balance < cost {
|
if client_account.balance < cost {
|
||||||
trace!(target: "txqueue",
|
trace!(target: "txqueue",
|
||||||
"Dropping transaction without sufficient balance: {:?} ({} < {})",
|
"Dropping transaction without sufficient balance: {:?} ({} < {})",
|
||||||
@ -818,7 +818,6 @@ impl TransactionQueue {
|
|||||||
balance: client_account.balance
|
balance: client_account.balance
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let r = self.import_tx(vtx, client_account.nonce).map_err(Error::Transaction);
|
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());
|
assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len());
|
||||||
r
|
r
|
||||||
@ -1071,11 +1070,11 @@ impl TransactionQueue {
|
|||||||
|
|
||||||
/// Returns top transactions from the queue ordered by priority.
|
/// Returns top transactions from the queue ordered by priority.
|
||||||
pub fn top_transactions(&self) -> Vec<SignedTransaction> {
|
pub fn top_transactions(&self) -> Vec<SignedTransaction> {
|
||||||
self.top_transactions_at(BlockNumber::max_value())
|
self.top_transactions_at(BlockNumber::max_value(), u64::max_value())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn filter_pending_transaction<F>(&self, best_block: BlockNumber, mut f: F)
|
fn filter_pending_transaction<F>(&self, best_block: BlockNumber, best_timestamp: u64, mut f: F)
|
||||||
where F: FnMut(&VerifiedTransaction) {
|
where F: FnMut(&VerifiedTransaction) {
|
||||||
|
|
||||||
let mut delayed = HashSet::new();
|
let mut delayed = HashSet::new();
|
||||||
@ -1085,7 +1084,12 @@ impl TransactionQueue {
|
|||||||
if delayed.contains(&sender) {
|
if delayed.contains(&sender) {
|
||||||
continue;
|
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);
|
delayed.insert(sender);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1094,16 +1098,16 @@ impl TransactionQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns top transactions from the queue ordered by priority.
|
/// Returns top transactions from the queue ordered by priority.
|
||||||
pub fn top_transactions_at(&self, best_block: BlockNumber) -> Vec<SignedTransaction> {
|
pub fn top_transactions_at(&self, best_block: BlockNumber, best_timestamp: u64) -> Vec<SignedTransaction> {
|
||||||
let mut r = Vec::new();
|
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
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return all ready transactions.
|
/// Return all ready transactions.
|
||||||
pub fn pending_transactions(&self, best_block: BlockNumber) -> Vec<PendingTransaction> {
|
pub fn pending_transactions(&self, best_block: BlockNumber, best_timestamp: u64) -> Vec<PendingTransaction> {
|
||||||
let mut r = Vec::new();
|
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
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1112,7 +1116,7 @@ impl TransactionQueue {
|
|||||||
self.future.by_priority
|
self.future.by_priority
|
||||||
.iter()
|
.iter()
|
||||||
.map(|t| self.by_hash.get(&t.hash).expect("All transactions in `current` and `future` are always included in `by_hash`"))
|
.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()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1385,7 +1389,7 @@ pub mod test {
|
|||||||
use super::{TransactionSet, TransactionOrder, VerifiedTransaction};
|
use super::{TransactionSet, TransactionOrder, VerifiedTransaction};
|
||||||
use miner::local_transactions::LocalTransactionsList;
|
use miner::local_transactions::LocalTransactionsList;
|
||||||
use client::TransactionImportResult;
|
use client::TransactionImportResult;
|
||||||
use transaction::{SignedTransaction, Transaction, Action};
|
use transaction::{SignedTransaction, Transaction, Action, Condition};
|
||||||
|
|
||||||
pub struct DummyTransactionDetailsProvider {
|
pub struct DummyTransactionDetailsProvider {
|
||||||
account_details: AccountDetails,
|
account_details: AccountDetails,
|
||||||
@ -2204,15 +2208,15 @@ pub mod test {
|
|||||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
|
|
||||||
// when
|
// 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();
|
let res2 = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(res1, TransactionImportResult::Current);
|
assert_eq!(res1, TransactionImportResult::Current);
|
||||||
assert_eq!(res2, 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);
|
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);
|
assert_eq!(top.len(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ use spec::Spec;
|
|||||||
use views::BlockView;
|
use views::BlockView;
|
||||||
use util::stats::Histogram;
|
use util::stats::Histogram;
|
||||||
use ethkey::{KeyPair, Secret};
|
use ethkey::{KeyPair, Secret};
|
||||||
use transaction::{PendingTransaction, Transaction, Action};
|
use transaction::{PendingTransaction, Transaction, Action, Condition};
|
||||||
use miner::MinerService;
|
use miner::MinerService;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -299,7 +299,7 @@ fn does_not_propagate_delayed_transactions() {
|
|||||||
action: Action::Call(Address::default()),
|
action: Action::Call(Address::default()),
|
||||||
value: 0.into(),
|
value: 0.into(),
|
||||||
data: Vec::new(),
|
data: Vec::new(),
|
||||||
}.sign(secret, None), Some(2));
|
}.sign(secret, None), Some(Condition::Number(2)));
|
||||||
let tx1 = PendingTransaction::new(Transaction {
|
let tx1 = PendingTransaction::new(Transaction {
|
||||||
nonce: 1.into(),
|
nonce: 1.into(),
|
||||||
gas_price: 0.into(),
|
gas_price: 0.into(),
|
||||||
|
@ -34,6 +34,8 @@ pub struct BlockChainInfo {
|
|||||||
pub best_block_hash: H256,
|
pub best_block_hash: H256,
|
||||||
/// Best blockchain block number.
|
/// Best blockchain block number.
|
||||||
pub best_block_number: BlockNumber,
|
pub best_block_number: BlockNumber,
|
||||||
|
/// Best blockchain block timestamp.
|
||||||
|
pub best_block_timestamp: u64,
|
||||||
/// Best ancient block hash.
|
/// Best ancient block hash.
|
||||||
pub ancient_block_hash: Option<H256>,
|
pub ancient_block_hash: Option<H256>,
|
||||||
/// Best ancient block number.
|
/// Best ancient block number.
|
||||||
|
@ -53,6 +53,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
|
/// A set of information describing an externally-originating message call
|
||||||
/// or contract creation operation.
|
/// or contract creation operation.
|
||||||
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||||
@ -407,16 +417,16 @@ impl Deref for LocalizedTransaction {
|
|||||||
pub struct PendingTransaction {
|
pub struct PendingTransaction {
|
||||||
/// Signed transaction data.
|
/// Signed transaction data.
|
||||||
pub transaction: SignedTransaction,
|
pub transaction: SignedTransaction,
|
||||||
/// To be activated at this block. `None` for immediately.
|
/// To be activated at this condition. `None` for immediately.
|
||||||
pub min_block: Option<BlockNumber>,
|
pub condition: Option<Condition>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PendingTransaction {
|
impl PendingTransaction {
|
||||||
/// Create a new pending transaction from signed transaction.
|
/// Create a new pending transaction from signed transaction.
|
||||||
pub fn new(signed: SignedTransaction, min_block: Option<BlockNumber>) -> Self {
|
pub fn new(signed: SignedTransaction, condition: Option<Condition>) -> Self {
|
||||||
PendingTransaction {
|
PendingTransaction {
|
||||||
transaction: signed,
|
transaction: signed,
|
||||||
min_block: min_block,
|
condition: condition,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -425,7 +435,7 @@ impl From<SignedTransaction> for PendingTransaction {
|
|||||||
fn from(t: SignedTransaction) -> Self {
|
fn from(t: SignedTransaction) -> Self {
|
||||||
PendingTransaction {
|
PendingTransaction {
|
||||||
transaction: t,
|
transaction: t,
|
||||||
min_block: None,
|
condition: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,7 +190,7 @@ pub fn sign_and_dispatch<C, M>(client: &C, miner: &M, accounts: &AccountProvider
|
|||||||
{
|
{
|
||||||
|
|
||||||
let network_id = client.signing_network_id();
|
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 = sign_no_dispatch(client, miner, accounts, filled, password)?;
|
||||||
|
|
||||||
let (signed_transaction, token) = match signed_transaction {
|
let (signed_transaction, token) = match signed_transaction {
|
||||||
@ -199,7 +199,7 @@ pub fn sign_and_dispatch<C, M>(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);
|
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| {
|
dispatch_transaction(&*client, &*miner, pending_transaction).map(|hash| {
|
||||||
match token {
|
match token {
|
||||||
Some(ref token) => WithToken::Yes(hash, token.clone()),
|
Some(ref token) => WithToken::Yes(hash, token.clone()),
|
||||||
@ -219,7 +219,7 @@ pub fn fill_optional_fields<C, M>(request: TransactionRequest, client: &C, miner
|
|||||||
gas: request.gas.unwrap_or_else(|| miner.sensible_gas_limit()),
|
gas: request.gas.unwrap_or_else(|| miner.sensible_gas_limit()),
|
||||||
value: request.value.unwrap_or_else(|| 0.into()),
|
value: request.value.unwrap_or_else(|| 0.into()),
|
||||||
data: request.data.unwrap_or_else(Vec::new),
|
data: request.data.unwrap_or_else(Vec::new),
|
||||||
min_block: request.min_block,
|
condition: request.condition,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use util::{Address, U256, Bytes, H256};
|
use util::{Address, U256, Bytes, H256};
|
||||||
|
use v1::types::TransactionCondition;
|
||||||
|
|
||||||
/// Transaction request coming from RPC
|
/// Transaction request coming from RPC
|
||||||
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
|
||||||
@ -33,8 +34,8 @@ pub struct TransactionRequest {
|
|||||||
pub data: Option<Bytes>,
|
pub data: Option<Bytes>,
|
||||||
/// Transaction's nonce
|
/// Transaction's nonce
|
||||||
pub nonce: Option<U256>,
|
pub nonce: Option<U256>,
|
||||||
/// Delay until this block if specified.
|
/// Delay until this condition is met.
|
||||||
pub min_block: Option<u64>,
|
pub condition: Option<TransactionCondition>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transaction request coming from RPC with default values filled in.
|
/// Transaction request coming from RPC with default values filled in.
|
||||||
@ -54,8 +55,8 @@ pub struct FilledTransactionRequest {
|
|||||||
pub data: Bytes,
|
pub data: Bytes,
|
||||||
/// Transaction's nonce
|
/// Transaction's nonce
|
||||||
pub nonce: Option<U256>,
|
pub nonce: Option<U256>,
|
||||||
/// Delay until this block if specified.
|
/// Delay until this condition is met.
|
||||||
pub min_block: Option<u64>,
|
pub condition: Option<TransactionCondition>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<FilledTransactionRequest> for TransactionRequest {
|
impl From<FilledTransactionRequest> for TransactionRequest {
|
||||||
@ -68,7 +69,7 @@ impl From<FilledTransactionRequest> for TransactionRequest {
|
|||||||
value: Some(r.value),
|
value: Some(r.value),
|
||||||
data: Some(r.data),
|
data: Some(r.data),
|
||||||
nonce: r.nonce,
|
nonce: r.nonce,
|
||||||
min_block: r.min_block,
|
condition: r.condition,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -325,7 +325,7 @@ mod test {
|
|||||||
value: 10_000_000.into(),
|
value: 10_000_000.into(),
|
||||||
data: vec![],
|
data: vec![],
|
||||||
nonce: None,
|
nonce: None,
|
||||||
min_block: None,
|
condition: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,8 +82,8 @@ impl<C: 'static, M: 'static> SignerClient<C, M> where C: MiningBlockChainClient,
|
|||||||
if let Some(gas) = modification.gas {
|
if let Some(gas) = modification.gas {
|
||||||
request.gas = gas.into();
|
request.gas = gas.into();
|
||||||
}
|
}
|
||||||
if let Some(ref min_block) = modification.min_block {
|
if let Some(ref condition) = modification.condition {
|
||||||
request.min_block = min_block.as_ref().and_then(|b| b.to_min_block_num());
|
request.condition = condition.clone().map(Into::into);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let result = f(&*client, &*miner, &*accounts, payload);
|
let result = f(&*client, &*miner, &*accounts, payload);
|
||||||
@ -154,7 +154,7 @@ impl<C: 'static, M: 'static> Signer for SignerClient<C, M> where C: MiningBlockC
|
|||||||
|
|
||||||
// Dispatch if everything is ok
|
// Dispatch if everything is ok
|
||||||
if sender_matches && data_matches && value_matches && nonce_matches {
|
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)
|
dispatch_transaction(&*client, &*miner, pending_transaction)
|
||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
.map(ConfirmationResponse::SendTransaction)
|
.map(ConfirmationResponse::SendTransaction)
|
||||||
|
@ -212,7 +212,7 @@ impl MinerService for TestMinerService {
|
|||||||
self.local_transactions.lock().iter().map(|(hash, stats)| (*hash, stats.clone())).collect()
|
self.local_transactions.lock().iter().map(|(hash, stats)| (*hash, stats.clone())).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ready_transactions(&self, _best_block: BlockNumber) -> Vec<PendingTransaction> {
|
fn ready_transactions(&self, _best_block: BlockNumber, _best_timestamp: u64) -> Vec<PendingTransaction> {
|
||||||
self.pending_transactions.lock().values().cloned().map(Into::into).collect()
|
self.pending_transactions.lock().values().cloned().map(Into::into).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -504,7 +504,7 @@ fn rpc_eth_pending_transaction_by_hash() {
|
|||||||
tester.miner.pending_transactions.lock().insert(H256::zero(), tx);
|
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#"{
|
let request = r#"{
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"method": "eth_getTransactionByHash",
|
"method": "eth_getTransactionByHash",
|
||||||
@ -820,12 +820,11 @@ fn rpc_eth_sign_transaction() {
|
|||||||
let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() +
|
let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() +
|
||||||
r#""raw":"0x"# + &rlp.to_hex() + r#"","# +
|
r#""raw":"0x"# + &rlp.to_hex() + r#"","# +
|
||||||
r#""tx":{"# +
|
r#""tx":{"# +
|
||||||
r#""blockHash":null,"blockNumber":null,"creates":null,"# +
|
r#""blockHash":null,"blockNumber":null,"condition":null,"creates":null,"# +
|
||||||
&format!("\"from\":\"0x{:?}\",", &address) +
|
&format!("\"from\":\"0x{:?}\",", &address) +
|
||||||
r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# +
|
r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# +
|
||||||
&format!("\"hash\":\"0x{:?}\",", t.hash()) +
|
&format!("\"hash\":\"0x{:?}\",", t.hash()) +
|
||||||
r#""input":"0x","# +
|
r#""input":"0x","# +
|
||||||
r#""minBlock":null,"# +
|
|
||||||
&format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) +
|
&format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) +
|
||||||
r#""nonce":"0x1","# +
|
r#""nonce":"0x1","# +
|
||||||
&format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) +
|
&format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) +
|
||||||
|
@ -83,7 +83,7 @@ fn should_return_list_of_items_to_confirm() {
|
|||||||
value: U256::from(1),
|
value: U256::from(1),
|
||||||
data: vec![],
|
data: vec![],
|
||||||
nonce: None,
|
nonce: None,
|
||||||
min_block: None,
|
condition: None,
|
||||||
})).unwrap();
|
})).unwrap();
|
||||||
tester.signer.add_request(ConfirmationPayload::Signature(1.into(), 5.into())).unwrap();
|
tester.signer.add_request(ConfirmationPayload::Signature(1.into(), 5.into())).unwrap();
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ fn should_return_list_of_items_to_confirm() {
|
|||||||
let request = r#"{"jsonrpc":"2.0","method":"signer_requestsToConfirm","params":[],"id":1}"#;
|
let request = r#"{"jsonrpc":"2.0","method":"signer_requestsToConfirm","params":[],"id":1}"#;
|
||||||
let response = concat!(
|
let response = concat!(
|
||||||
r#"{"jsonrpc":"2.0","result":["#,
|
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","hash":"0x0000000000000000000000000000000000000000000000000000000000000005"}}}"#,
|
r#"{"id":"0x2","payload":{"sign":{"address":"0x0000000000000000000000000000000000000001","hash":"0x0000000000000000000000000000000000000000000000000000000000000005"}}}"#,
|
||||||
r#"],"id":1}"#
|
r#"],"id":1}"#
|
||||||
);
|
);
|
||||||
@ -113,7 +113,7 @@ fn should_reject_transaction_from_queue_without_dispatching() {
|
|||||||
value: U256::from(1),
|
value: U256::from(1),
|
||||||
data: vec![],
|
data: vec![],
|
||||||
nonce: None,
|
nonce: None,
|
||||||
min_block: None,
|
condition: None,
|
||||||
})).unwrap();
|
})).unwrap();
|
||||||
assert_eq!(tester.signer.requests().len(), 1);
|
assert_eq!(tester.signer.requests().len(), 1);
|
||||||
|
|
||||||
@ -139,7 +139,7 @@ fn should_not_remove_transaction_if_password_is_invalid() {
|
|||||||
value: U256::from(1),
|
value: U256::from(1),
|
||||||
data: vec![],
|
data: vec![],
|
||||||
nonce: None,
|
nonce: None,
|
||||||
min_block: None,
|
condition: None,
|
||||||
})).unwrap();
|
})).unwrap();
|
||||||
assert_eq!(tester.signer.requests().len(), 1);
|
assert_eq!(tester.signer.requests().len(), 1);
|
||||||
|
|
||||||
@ -182,7 +182,7 @@ fn should_confirm_transaction_and_dispatch() {
|
|||||||
value: U256::from(1),
|
value: U256::from(1),
|
||||||
data: vec![],
|
data: vec![],
|
||||||
nonce: None,
|
nonce: None,
|
||||||
min_block: None,
|
condition: None,
|
||||||
})).unwrap();
|
})).unwrap();
|
||||||
|
|
||||||
let t = Transaction {
|
let t = Transaction {
|
||||||
@ -228,7 +228,7 @@ fn should_confirm_transaction_with_token() {
|
|||||||
value: U256::from(1),
|
value: U256::from(1),
|
||||||
data: vec![],
|
data: vec![],
|
||||||
nonce: None,
|
nonce: None,
|
||||||
min_block: None,
|
condition: None,
|
||||||
})).unwrap();
|
})).unwrap();
|
||||||
|
|
||||||
let t = Transaction {
|
let t = Transaction {
|
||||||
@ -276,7 +276,7 @@ fn should_confirm_transaction_with_rlp() {
|
|||||||
value: U256::from(1),
|
value: U256::from(1),
|
||||||
data: vec![],
|
data: vec![],
|
||||||
nonce: None,
|
nonce: None,
|
||||||
min_block: None,
|
condition: None,
|
||||||
})).unwrap();
|
})).unwrap();
|
||||||
|
|
||||||
let t = Transaction {
|
let t = Transaction {
|
||||||
@ -324,7 +324,7 @@ fn should_return_error_when_sender_does_not_match() {
|
|||||||
value: U256::from(1),
|
value: U256::from(1),
|
||||||
data: vec![],
|
data: vec![],
|
||||||
nonce: None,
|
nonce: None,
|
||||||
min_block: None,
|
condition: None,
|
||||||
})).unwrap();
|
})).unwrap();
|
||||||
|
|
||||||
let t = Transaction {
|
let t = Transaction {
|
||||||
|
@ -280,12 +280,11 @@ fn should_add_sign_transaction_to_the_queue() {
|
|||||||
let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() +
|
let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() +
|
||||||
r#""raw":"0x"# + &rlp.to_hex() + r#"","# +
|
r#""raw":"0x"# + &rlp.to_hex() + r#"","# +
|
||||||
r#""tx":{"# +
|
r#""tx":{"# +
|
||||||
r#""blockHash":null,"blockNumber":null,"creates":null,"# +
|
r#""blockHash":null,"blockNumber":null,"condition":null,"creates":null,"# +
|
||||||
&format!("\"from\":\"0x{:?}\",", &address) +
|
&format!("\"from\":\"0x{:?}\",", &address) +
|
||||||
r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# +
|
r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# +
|
||||||
&format!("\"hash\":\"0x{:?}\",", t.hash()) +
|
&format!("\"hash\":\"0x{:?}\",", t.hash()) +
|
||||||
r#""input":"0x","# +
|
r#""input":"0x","# +
|
||||||
r#""minBlock":null,"# +
|
|
||||||
&format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) +
|
&format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) +
|
||||||
r#""nonce":"0x1","# +
|
r#""nonce":"0x1","# +
|
||||||
&format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) +
|
&format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) +
|
||||||
|
@ -139,7 +139,7 @@ mod tests {
|
|||||||
fn test_serialize_block_transactions() {
|
fn test_serialize_block_transactions() {
|
||||||
let t = BlockTransactions::Full(vec![Transaction::default()]);
|
let t = BlockTransactions::Full(vec![Transaction::default()]);
|
||||||
let serialized = serde_json::to_string(&t).unwrap();
|
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 t = BlockTransactions::Hashes(vec![H256::default().into()]);
|
||||||
let serialized = serde_json::to_string(&t).unwrap();
|
let serialized = serde_json::to_string(&t).unwrap();
|
||||||
|
@ -20,7 +20,7 @@ use std::fmt;
|
|||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
use util::log::Colour;
|
use util::log::Colour;
|
||||||
|
|
||||||
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;
|
use v1::helpers;
|
||||||
|
|
||||||
/// Confirmation waiting in a queue
|
/// Confirmation waiting in a queue
|
||||||
@ -193,9 +193,8 @@ pub struct TransactionModification {
|
|||||||
pub gas_price: Option<U256>,
|
pub gas_price: Option<U256>,
|
||||||
/// Modified gas
|
/// Modified gas
|
||||||
pub gas: Option<U256>,
|
pub gas: Option<U256>,
|
||||||
/// Modified min block
|
/// Modified transaction condition.
|
||||||
#[serde(rename="minBlock")]
|
pub condition: Option<Option<TransactionCondition>>,
|
||||||
pub min_block: Option<Option<BlockNumber>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents two possible return values.
|
/// Represents two possible return values.
|
||||||
@ -237,7 +236,7 @@ impl<A, B> Serialize for Either<A, B> where
|
|||||||
mod tests {
|
mod tests {
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use v1::types::{U256, H256, BlockNumber};
|
use v1::types::{U256, H256, TransactionCondition};
|
||||||
use v1::helpers;
|
use v1::helpers;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -270,13 +269,13 @@ mod tests {
|
|||||||
value: 100_000.into(),
|
value: 100_000.into(),
|
||||||
data: vec![1, 2, 3],
|
data: vec![1, 2, 3],
|
||||||
nonce: Some(1.into()),
|
nonce: Some(1.into()),
|
||||||
min_block: None,
|
condition: None,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let res = serde_json::to_string(&ConfirmationRequest::from(request));
|
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
|
// then
|
||||||
assert_eq!(res.unwrap(), expected.to_owned());
|
assert_eq!(res.unwrap(), expected.to_owned());
|
||||||
@ -295,13 +294,13 @@ mod tests {
|
|||||||
value: 100_000.into(),
|
value: 100_000.into(),
|
||||||
data: vec![1, 2, 3],
|
data: vec![1, 2, 3],
|
||||||
nonce: Some(1.into()),
|
nonce: Some(1.into()),
|
||||||
min_block: None,
|
condition: None,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let res = serde_json::to_string(&ConfirmationRequest::from(request));
|
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
|
// then
|
||||||
assert_eq!(res.unwrap(), expected.to_owned());
|
assert_eq!(res.unwrap(), expected.to_owned());
|
||||||
@ -330,7 +329,7 @@ mod tests {
|
|||||||
// given
|
// given
|
||||||
let s1 = r#"{
|
let s1 = r#"{
|
||||||
"gasPrice":"0xba43b7400",
|
"gasPrice":"0xba43b7400",
|
||||||
"minBlock":"0x42"
|
"condition": { "block": 66 }
|
||||||
}"#;
|
}"#;
|
||||||
let s2 = r#"{"gas": "0x1233"}"#;
|
let s2 = r#"{"gas": "0x1233"}"#;
|
||||||
let s3 = r#"{}"#;
|
let s3 = r#"{}"#;
|
||||||
@ -344,17 +343,17 @@ mod tests {
|
|||||||
assert_eq!(res1, TransactionModification {
|
assert_eq!(res1, TransactionModification {
|
||||||
gas_price: Some(U256::from_str("0ba43b7400").unwrap()),
|
gas_price: Some(U256::from_str("0ba43b7400").unwrap()),
|
||||||
gas: None,
|
gas: None,
|
||||||
min_block: Some(Some(BlockNumber::Num(0x42))),
|
condition: Some(Some(TransactionCondition::Number(0x42))),
|
||||||
});
|
});
|
||||||
assert_eq!(res2, TransactionModification {
|
assert_eq!(res2, TransactionModification {
|
||||||
gas_price: None,
|
gas_price: None,
|
||||||
gas: Some(U256::from_str("1233").unwrap()),
|
gas: Some(U256::from_str("1233").unwrap()),
|
||||||
min_block: None,
|
condition: None,
|
||||||
});
|
});
|
||||||
assert_eq!(res3, TransactionModification {
|
assert_eq!(res3, TransactionModification {
|
||||||
gas_price: None,
|
gas_price: None,
|
||||||
gas: None,
|
gas: None,
|
||||||
min_block: None,
|
condition: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ mod log;
|
|||||||
mod sync;
|
mod sync;
|
||||||
mod transaction;
|
mod transaction;
|
||||||
mod transaction_request;
|
mod transaction_request;
|
||||||
|
mod transaction_condition;
|
||||||
mod receipt;
|
mod receipt;
|
||||||
mod rpc_settings;
|
mod rpc_settings;
|
||||||
mod trace;
|
mod trace;
|
||||||
@ -55,6 +56,7 @@ pub use self::sync::{
|
|||||||
};
|
};
|
||||||
pub use self::transaction::{Transaction, RichRawTransaction, LocalTransactionStatus};
|
pub use self::transaction::{Transaction, RichRawTransaction, LocalTransactionStatus};
|
||||||
pub use self::transaction_request::TransactionRequest;
|
pub use self::transaction_request::TransactionRequest;
|
||||||
|
pub use self::transaction_condition::TransactionCondition;
|
||||||
pub use self::receipt::Receipt;
|
pub use self::receipt::Receipt;
|
||||||
pub use self::rpc_settings::RpcSettings;
|
pub use self::rpc_settings::RpcSettings;
|
||||||
pub use self::trace::{LocalizedTrace, TraceResults};
|
pub use self::trace::{LocalizedTrace, TraceResults};
|
||||||
|
@ -19,7 +19,7 @@ use ethcore::miner;
|
|||||||
use ethcore::contract_address;
|
use ethcore::contract_address;
|
||||||
use ethcore::transaction::{LocalizedTransaction, Action, PendingTransaction, SignedTransaction};
|
use ethcore::transaction::{LocalizedTransaction, Action, PendingTransaction, SignedTransaction};
|
||||||
use v1::helpers::errors;
|
use v1::helpers::errors;
|
||||||
use v1::types::{Bytes, H160, H256, U256, H512, BlockNumber};
|
use v1::types::{Bytes, H160, H256, U256, H512, TransactionCondition};
|
||||||
|
|
||||||
/// Transaction
|
/// Transaction
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Serialize)]
|
#[derive(Debug, Default, Clone, PartialEq, Serialize)]
|
||||||
@ -70,8 +70,7 @@ pub struct Transaction {
|
|||||||
/// The S field of the signature.
|
/// The S field of the signature.
|
||||||
pub s: U256,
|
pub s: U256,
|
||||||
/// Transaction activates at specified block.
|
/// Transaction activates at specified block.
|
||||||
#[serde(rename="minBlock")]
|
pub condition: Option<TransactionCondition>,
|
||||||
pub min_block: Option<BlockNumber>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Local Transaction Status
|
/// Local Transaction Status
|
||||||
@ -190,7 +189,7 @@ impl From<LocalizedTransaction> for Transaction {
|
|||||||
v: t.original_v().into(),
|
v: t.original_v().into(),
|
||||||
r: signature.r().into(),
|
r: signature.r().into(),
|
||||||
s: signature.s().into(),
|
s: signature.s().into(),
|
||||||
min_block: None,
|
condition: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -224,7 +223,7 @@ impl From<SignedTransaction> for Transaction {
|
|||||||
v: t.original_v().into(),
|
v: t.original_v().into(),
|
||||||
r: signature.r().into(),
|
r: signature.r().into(),
|
||||||
s: signature.s().into(),
|
s: signature.s().into(),
|
||||||
min_block: None,
|
condition: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -232,7 +231,7 @@ impl From<SignedTransaction> for Transaction {
|
|||||||
impl From<PendingTransaction> for Transaction {
|
impl From<PendingTransaction> for Transaction {
|
||||||
fn from(t: PendingTransaction) -> Transaction {
|
fn from(t: PendingTransaction) -> Transaction {
|
||||||
let mut r = Transaction::from(t.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
|
r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -261,7 +260,7 @@ mod tests {
|
|||||||
fn test_transaction_serialize() {
|
fn test_transaction_serialize() {
|
||||||
let t = Transaction::default();
|
let t = Transaction::default();
|
||||||
let serialized = serde_json::to_string(&t).unwrap();
|
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]
|
#[test]
|
||||||
|
67
rpc/src/v1/types/transaction_condition.rs
Normal file
67
rpc/src/v1/types/transaction_condition.rs
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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<ethcore::transaction::Condition> 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<ethcore::transaction::Condition> 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<TransactionCondition> = 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
//! `TransactionRequest` type
|
//! `TransactionRequest` type
|
||||||
|
|
||||||
use v1::types::{Bytes, H160, U256, BlockNumber};
|
use v1::types::{Bytes, H160, U256, TransactionCondition};
|
||||||
use v1::helpers;
|
use v1::helpers;
|
||||||
use util::log::Colour;
|
use util::log::Colour;
|
||||||
|
|
||||||
@ -41,9 +41,8 @@ pub struct TransactionRequest {
|
|||||||
pub data: Option<Bytes>,
|
pub data: Option<Bytes>,
|
||||||
/// Transaction's nonce
|
/// Transaction's nonce
|
||||||
pub nonce: Option<U256>,
|
pub nonce: Option<U256>,
|
||||||
/// Delay until this block if specified.
|
/// Delay until this block condition.
|
||||||
#[serde(rename="minBlock")]
|
pub condition: Option<TransactionCondition>,
|
||||||
pub min_block: Option<BlockNumber>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format_ether(i: U256) -> String {
|
pub fn format_ether(i: U256) -> String {
|
||||||
@ -93,7 +92,7 @@ impl From<helpers::TransactionRequest> for TransactionRequest {
|
|||||||
value: r.value.map(Into::into),
|
value: r.value.map(Into::into),
|
||||||
data: r.data.map(Into::into),
|
data: r.data.map(Into::into),
|
||||||
nonce: r.nonce.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<helpers::FilledTransactionRequest> for TransactionRequest {
|
|||||||
value: Some(r.value.into()),
|
value: Some(r.value.into()),
|
||||||
data: Some(r.data.into()),
|
data: Some(r.data.into()),
|
||||||
nonce: r.nonce.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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,7 +122,7 @@ impl Into<helpers::TransactionRequest> for TransactionRequest {
|
|||||||
value: self.value.map(Into::into),
|
value: self.value.map(Into::into),
|
||||||
data: self.data.map(Into::into),
|
data: self.data.map(Into::into),
|
||||||
nonce: self.nonce.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 std::str::FromStr;
|
||||||
use rustc_serialize::hex::FromHex;
|
use rustc_serialize::hex::FromHex;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use v1::types::{U256, H160, BlockNumber};
|
use v1::types::{U256, H160, TransactionCondition};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -147,7 +146,7 @@ mod tests {
|
|||||||
"value":"0x3",
|
"value":"0x3",
|
||||||
"data":"0x123456",
|
"data":"0x123456",
|
||||||
"nonce":"0x4",
|
"nonce":"0x4",
|
||||||
"minBlock":"0x13"
|
"condition": { "block": 19 }
|
||||||
}"#;
|
}"#;
|
||||||
let deserialized: TransactionRequest = serde_json::from_str(s).unwrap();
|
let deserialized: TransactionRequest = serde_json::from_str(s).unwrap();
|
||||||
|
|
||||||
@ -159,7 +158,7 @@ mod tests {
|
|||||||
value: Some(U256::from(3)),
|
value: Some(U256::from(3)),
|
||||||
data: Some(vec![0x12, 0x34, 0x56].into()),
|
data: Some(vec![0x12, 0x34, 0x56].into()),
|
||||||
nonce: Some(U256::from(4)),
|
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()),
|
value: Some(U256::from_str("9184e72a").unwrap()),
|
||||||
data: Some("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap().into()),
|
data: Some("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap().into()),
|
||||||
nonce: None,
|
nonce: None,
|
||||||
min_block: None,
|
condition: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,7 +199,7 @@ mod tests {
|
|||||||
value: None,
|
value: None,
|
||||||
data: None,
|
data: None,
|
||||||
nonce: None,
|
nonce: None,
|
||||||
min_block: None,
|
condition: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,7 +223,7 @@ mod tests {
|
|||||||
value: None,
|
value: None,
|
||||||
data: Some(vec![0x85, 0x95, 0xba, 0xb1].into()),
|
data: Some(vec![0x85, 0x95, 0xba, 0xb1].into()),
|
||||||
nonce: None,
|
nonce: None,
|
||||||
min_block: None,
|
condition: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use client::{Rpc, RpcError};
|
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 serde_json::{Value as JsonValue, to_value};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use futures::{BoxFuture, Canceled};
|
use futures::{BoxFuture, Canceled};
|
||||||
@ -22,13 +22,13 @@ impl SignerRpc {
|
|||||||
id: U256,
|
id: U256,
|
||||||
new_gas: Option<U256>,
|
new_gas: Option<U256>,
|
||||||
new_gas_price: Option<U256>,
|
new_gas_price: Option<U256>,
|
||||||
new_min_block: Option<Option<BlockNumber>>,
|
new_condition: Option<Option<TransactionCondition>>,
|
||||||
pwd: &str
|
pwd: &str
|
||||||
) -> BoxFuture<Result<U256, RpcError>, Canceled>
|
) -> BoxFuture<Result<U256, RpcError>, Canceled>
|
||||||
{
|
{
|
||||||
self.rpc.request("signer_confirmRequest", vec![
|
self.rpc.request("signer_confirmRequest", vec![
|
||||||
to_value(&format!("{:#x}", id)),
|
to_value(&format!("{:#x}", id)),
|
||||||
to_value(&TransactionModification { gas_price: new_gas_price, gas: new_gas, min_block: new_min_block }),
|
to_value(&TransactionModification { gas_price: new_gas_price, gas: new_gas, condition: new_condition }),
|
||||||
to_value(&pwd),
|
to_value(&pwd),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user