Merge branch 'cht-td' into lightrpc
This commit is contained in:
@@ -21,7 +21,7 @@ mod stores;
|
||||
use self::stores::{AddressBook, DappsSettingsStore, NewDappsPolicy};
|
||||
|
||||
use std::fmt;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::time::{Instant, Duration};
|
||||
use util::RwLock;
|
||||
use ethstore::{SimpleSecretStore, SecretStore, Error as SSError, EthStore, EthMultiStore,
|
||||
@@ -114,8 +114,8 @@ impl AccountProvider {
|
||||
pub fn new(sstore: Box<SecretStore>) -> Self {
|
||||
AccountProvider {
|
||||
unlocked: RwLock::new(HashMap::new()),
|
||||
address_book: RwLock::new(AddressBook::new(sstore.local_path().into())),
|
||||
dapps_settings: RwLock::new(DappsSettingsStore::new(sstore.local_path().into())),
|
||||
address_book: RwLock::new(AddressBook::new(&sstore.local_path())),
|
||||
dapps_settings: RwLock::new(DappsSettingsStore::new(&sstore.local_path())),
|
||||
sstore: sstore,
|
||||
transient_sstore: transient_sstore(),
|
||||
}
|
||||
@@ -216,7 +216,7 @@ impl AccountProvider {
|
||||
Some(accounts) => Ok(accounts),
|
||||
None => match dapps.policy() {
|
||||
NewDappsPolicy::AllAccounts => self.accounts(),
|
||||
NewDappsPolicy::Whitelist(accounts) => Ok(accounts),
|
||||
NewDappsPolicy::Whitelist(accounts) => self.filter_addresses(accounts),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -231,28 +231,42 @@ impl AccountProvider {
|
||||
|
||||
/// Sets addresses visile for dapp.
|
||||
pub fn set_dapps_addresses(&self, dapp: DappId, addresses: Vec<Address>) -> Result<(), Error> {
|
||||
let addresses = self.filter_addresses(addresses)?;
|
||||
self.dapps_settings.write().set_accounts(dapp, addresses);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns each address along with metadata.
|
||||
pub fn addresses_info(&self) -> Result<HashMap<Address, AccountMeta>, Error> {
|
||||
Ok(self.address_book.read().get())
|
||||
/// Removes addresses that are neither accounts nor in address book.
|
||||
fn filter_addresses(&self, addresses: Vec<Address>) -> Result<Vec<Address>, Error> {
|
||||
let valid = self.addresses_info().into_iter()
|
||||
.map(|(address, _)| address)
|
||||
.chain(self.accounts()?)
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
Ok(addresses.into_iter()
|
||||
.filter(|a| valid.contains(&a))
|
||||
.collect()
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns each address along with metadata.
|
||||
pub fn set_address_name(&self, account: Address, name: String) -> Result<(), Error> {
|
||||
Ok(self.address_book.write().set_name(account, name))
|
||||
pub fn addresses_info(&self) -> HashMap<Address, AccountMeta> {
|
||||
self.address_book.read().get()
|
||||
}
|
||||
|
||||
/// Returns each address along with metadata.
|
||||
pub fn set_address_meta(&self, account: Address, meta: String) -> Result<(), Error> {
|
||||
Ok(self.address_book.write().set_meta(account, meta))
|
||||
pub fn set_address_name(&self, account: Address, name: String) {
|
||||
self.address_book.write().set_name(account, name)
|
||||
}
|
||||
|
||||
/// Returns each address along with metadata.
|
||||
pub fn set_address_meta(&self, account: Address, meta: String) {
|
||||
self.address_book.write().set_meta(account, meta)
|
||||
}
|
||||
|
||||
/// Removes and address from the addressbook
|
||||
pub fn remove_address(&self, addr: Address) -> Result<(), Error> {
|
||||
Ok(self.address_book.write().remove(addr))
|
||||
pub fn remove_address(&self, addr: Address) {
|
||||
self.address_book.write().remove(addr)
|
||||
}
|
||||
|
||||
/// Returns each account along with name and meta.
|
||||
@@ -502,9 +516,12 @@ mod tests {
|
||||
let app = DappId("app1".into());
|
||||
// set `AllAccounts` policy
|
||||
ap.set_new_dapps_whitelist(None).unwrap();
|
||||
// add accounts to address book
|
||||
ap.set_address_name(1.into(), "1".into());
|
||||
ap.set_address_name(2.into(), "2".into());
|
||||
|
||||
// when
|
||||
ap.set_dapps_addresses(app.clone(), vec![1.into(), 2.into()]).unwrap();
|
||||
ap.set_dapps_addresses(app.clone(), vec![1.into(), 2.into(), 3.into()]).unwrap();
|
||||
|
||||
// then
|
||||
assert_eq!(ap.dapps_addresses(app.clone()).unwrap(), vec![1.into(), 2.into()]);
|
||||
@@ -515,6 +532,7 @@ mod tests {
|
||||
// given
|
||||
let ap = AccountProvider::transient_provider();
|
||||
let address = ap.new_account("test").unwrap();
|
||||
ap.set_address_name(1.into(), "1".into());
|
||||
|
||||
// When returning nothing
|
||||
ap.set_new_dapps_whitelist(Some(vec![])).unwrap();
|
||||
@@ -524,6 +542,10 @@ mod tests {
|
||||
ap.set_new_dapps_whitelist(None).unwrap();
|
||||
assert_eq!(ap.dapps_addresses("app1".into()).unwrap(), vec![address]);
|
||||
|
||||
// change to non-existent account
|
||||
ap.set_new_dapps_whitelist(Some(vec![2.into()])).unwrap();
|
||||
assert_eq!(ap.dapps_addresses("app1".into()).unwrap(), vec![]);
|
||||
|
||||
// change to a whitelist
|
||||
ap.set_new_dapps_whitelist(Some(vec![1.into()])).unwrap();
|
||||
assert_eq!(ap.dapps_addresses("app1".into()).unwrap(), vec![1.into()]);
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
use std::{fs, fmt, hash, ops};
|
||||
use std::sync::atomic::{self, AtomicUsize};
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use ethstore::ethkey::Address;
|
||||
use ethjson::misc::{
|
||||
@@ -37,9 +37,9 @@ pub struct AddressBook {
|
||||
|
||||
impl AddressBook {
|
||||
/// Creates new address book at given directory.
|
||||
pub fn new(path: String) -> Self {
|
||||
pub fn new(path: &Path) -> Self {
|
||||
let mut r = AddressBook {
|
||||
cache: DiskMap::new(path, "address_book.json".into())
|
||||
cache: DiskMap::new(path, "address_book.json")
|
||||
};
|
||||
r.cache.revert(AccountMeta::read);
|
||||
r
|
||||
@@ -200,11 +200,11 @@ pub struct DappsSettingsStore {
|
||||
|
||||
impl DappsSettingsStore {
|
||||
/// Creates new store at given directory path.
|
||||
pub fn new(path: String) -> Self {
|
||||
pub fn new(path: &Path) -> Self {
|
||||
let mut r = DappsSettingsStore {
|
||||
settings: DiskMap::new(path.clone(), "dapps_accounts.json".into()),
|
||||
policy: DiskMap::new(path.clone(), "dapps_policy.json".into()),
|
||||
history: DiskMap::new(path.clone(), "dapps_history.json".into()),
|
||||
settings: DiskMap::new(path, "dapps_accounts.json".into()),
|
||||
policy: DiskMap::new(path, "dapps_policy.json".into()),
|
||||
history: DiskMap::new(path, "dapps_history.json".into()),
|
||||
time: TimeProvider::Clock,
|
||||
};
|
||||
r.settings.revert(JsonSettings::read);
|
||||
@@ -297,9 +297,8 @@ impl<K: hash::Hash + Eq, V> ops::DerefMut for DiskMap<K, V> {
|
||||
}
|
||||
|
||||
impl<K: hash::Hash + Eq, V> DiskMap<K, V> {
|
||||
pub fn new(path: String, file_name: String) -> Self {
|
||||
trace!(target: "diskmap", "new({})", path);
|
||||
let mut path: PathBuf = path.into();
|
||||
pub fn new(path: &Path, file_name: &str) -> Self {
|
||||
let mut path = path.to_owned();
|
||||
path.push(file_name);
|
||||
trace!(target: "diskmap", "path={:?}", path);
|
||||
DiskMap {
|
||||
@@ -310,7 +309,7 @@ impl<K: hash::Hash + Eq, V> DiskMap<K, V> {
|
||||
}
|
||||
|
||||
pub fn transient() -> Self {
|
||||
let mut map = DiskMap::new(Default::default(), "diskmap.json".into());
|
||||
let mut map = DiskMap::new(&PathBuf::new(), "diskmap.json".into());
|
||||
map.transient = true;
|
||||
map
|
||||
}
|
||||
@@ -354,27 +353,25 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn should_save_and_reload_address_book() {
|
||||
let temp = RandomTempPath::create_dir();
|
||||
let path = temp.as_str().to_owned();
|
||||
let mut b = AddressBook::new(path.clone());
|
||||
let path = RandomTempPath::create_dir();
|
||||
let mut b = AddressBook::new(&path);
|
||||
b.set_name(1.into(), "One".to_owned());
|
||||
b.set_meta(1.into(), "{1:1}".to_owned());
|
||||
let b = AddressBook::new(path);
|
||||
let b = AddressBook::new(&path);
|
||||
assert_eq!(b.get(), hash_map![1.into() => AccountMeta{name: "One".to_owned(), meta: "{1:1}".to_owned(), uuid: None}]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_remove_address() {
|
||||
let temp = RandomTempPath::create_dir();
|
||||
let path = temp.as_str().to_owned();
|
||||
let mut b = AddressBook::new(path.clone());
|
||||
let path = RandomTempPath::create_dir();
|
||||
let mut b = AddressBook::new(&path);
|
||||
|
||||
b.set_name(1.into(), "One".to_owned());
|
||||
b.set_name(2.into(), "Two".to_owned());
|
||||
b.set_name(3.into(), "Three".to_owned());
|
||||
b.remove(2.into());
|
||||
|
||||
let b = AddressBook::new(path);
|
||||
let b = AddressBook::new(&path);
|
||||
assert_eq!(b.get(), hash_map![
|
||||
1.into() => AccountMeta{name: "One".to_owned(), meta: "{}".to_owned(), uuid: None},
|
||||
3.into() => AccountMeta{name: "Three".to_owned(), meta: "{}".to_owned(), uuid: None}
|
||||
@@ -384,15 +381,14 @@ mod tests {
|
||||
#[test]
|
||||
fn should_save_and_reload_dapps_settings() {
|
||||
// given
|
||||
let temp = RandomTempPath::create_dir();
|
||||
let path = temp.as_str().to_owned();
|
||||
let mut b = DappsSettingsStore::new(path.clone());
|
||||
let path = RandomTempPath::create_dir();
|
||||
let mut b = DappsSettingsStore::new(&path);
|
||||
|
||||
// when
|
||||
b.set_accounts("dappOne".into(), vec![1.into(), 2.into()]);
|
||||
|
||||
// then
|
||||
let b = DappsSettingsStore::new(path);
|
||||
let b = DappsSettingsStore::new(&path);
|
||||
assert_eq!(b.settings(), hash_map![
|
||||
"dappOne".into() => DappsSettings {
|
||||
accounts: vec![1.into(), 2.into()],
|
||||
@@ -422,9 +418,8 @@ mod tests {
|
||||
#[test]
|
||||
fn should_store_dapps_policy() {
|
||||
// given
|
||||
let temp = RandomTempPath::create_dir();
|
||||
let path = temp.as_str().to_owned();
|
||||
let mut store = DappsSettingsStore::new(path.clone());
|
||||
let path = RandomTempPath::create_dir();
|
||||
let mut store = DappsSettingsStore::new(&path);
|
||||
|
||||
// Test default policy
|
||||
assert_eq!(store.policy(), NewDappsPolicy::AllAccounts);
|
||||
@@ -433,7 +428,7 @@ mod tests {
|
||||
store.set_policy(NewDappsPolicy::Whitelist(vec![1.into(), 2.into()]));
|
||||
|
||||
// then
|
||||
let store = DappsSettingsStore::new(path);
|
||||
let store = DappsSettingsStore::new(&path);
|
||||
assert_eq!(store.policy.clone(), hash_map![
|
||||
"default".into() => NewDappsPolicy::Whitelist(vec![1.into(), 2.into()])
|
||||
]);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()),
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1406,7 +1406,11 @@ impl BlockChainClient for Client {
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -552,7 +552,7 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
let mut adding = false;
|
||||
|
||||
let mut blocks = Vec::new();
|
||||
for (_, hash) in numbers_read.iter().sort_by(|tuple1, tuple2| tuple1.0.cmp(tuple2.0)) {
|
||||
for (_, hash) in numbers_read.iter().sorted_by(|tuple1, tuple2| tuple1.0.cmp(tuple2.0)) {
|
||||
if hash == to {
|
||||
if adding {
|
||||
blocks.push(hash.clone());
|
||||
@@ -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<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 }
|
||||
|
||||
@@ -159,7 +159,7 @@ impl IoHandler<()> for TransitionHandler {
|
||||
fn initialize(&self, io: &IoContext<()>) {
|
||||
if let Some(engine) = self.engine.upgrade() {
|
||||
io.register_timer_once(ENGINE_TIMEOUT_TOKEN, engine.remaining_step_duration().as_millis())
|
||||
.unwrap_or_else(|e| warn!(target: "poa", "Failed to start consensus step timer: {}.", e))
|
||||
.unwrap_or_else(|e| warn!(target: "engine", "Failed to start consensus step timer: {}.", e))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ impl IoHandler<()> for TransitionHandler {
|
||||
if let Some(engine) = self.engine.upgrade() {
|
||||
engine.step();
|
||||
io.register_timer_once(ENGINE_TIMEOUT_TOKEN, engine.remaining_step_duration().as_millis())
|
||||
.unwrap_or_else(|e| warn!(target: "poa", "Failed to restart consensus step timer: {}.", e))
|
||||
.unwrap_or_else(|e| warn!(target: "engine", "Failed to restart consensus step timer: {}.", e))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -234,14 +234,14 @@ impl Engine for AuthorityRound {
|
||||
let step = self.step.load(AtomicOrdering::SeqCst);
|
||||
if self.is_step_proposer(step, header.author()) {
|
||||
if let Ok(signature) = self.signer.sign(header.bare_hash()) {
|
||||
trace!(target: "poa", "generate_seal: Issuing a block for step {}.", step);
|
||||
trace!(target: "engine", "generate_seal: Issuing a block for step {}.", step);
|
||||
self.proposed.store(true, AtomicOrdering::SeqCst);
|
||||
return Seal::Regular(vec![encode(&step).to_vec(), encode(&(&H520::from(signature) as &[u8])).to_vec()]);
|
||||
} else {
|
||||
warn!(target: "poa", "generate_seal: FAIL: Accounts secret key unavailable.");
|
||||
warn!(target: "engine", "generate_seal: FAIL: Accounts secret key unavailable.");
|
||||
}
|
||||
} else {
|
||||
trace!(target: "poa", "generate_seal: Not a proposer for step {}.", step);
|
||||
trace!(target: "engine", "generate_seal: Not a proposer for step {}.", step);
|
||||
}
|
||||
Seal::None
|
||||
}
|
||||
@@ -260,7 +260,7 @@ impl Engine for AuthorityRound {
|
||||
/// Check the number of seal fields.
|
||||
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||
if header.seal().len() != self.seal_fields() {
|
||||
trace!(target: "poa", "verify_block_basic: wrong number of seal fields");
|
||||
trace!(target: "engine", "verify_block_basic: wrong number of seal fields");
|
||||
Err(From::from(BlockError::InvalidSealArity(
|
||||
Mismatch { expected: self.seal_fields(), found: header.seal().len() }
|
||||
)))
|
||||
@@ -279,11 +279,11 @@ impl Engine for AuthorityRound {
|
||||
if verify_address(&correct_proposer, &proposer_signature, &header.bare_hash())? {
|
||||
Ok(())
|
||||
} else {
|
||||
trace!(target: "poa", "verify_block_unordered: bad proposer for step: {}", header_step);
|
||||
trace!(target: "engine", "verify_block_unordered: bad proposer for step: {}", header_step);
|
||||
Err(EngineError::NotProposer(Mismatch { expected: correct_proposer, found: header.author().clone() }))?
|
||||
}
|
||||
} else {
|
||||
trace!(target: "poa", "verify_block_unordered: block from the future");
|
||||
trace!(target: "engine", "verify_block_unordered: block from the future");
|
||||
self.validators.report_benign(header.author());
|
||||
Err(BlockError::InvalidSeal)?
|
||||
}
|
||||
@@ -297,7 +297,7 @@ impl Engine for AuthorityRound {
|
||||
let step = header_step(header)?;
|
||||
// Check if parent is from a previous step.
|
||||
if step == header_step(parent)? {
|
||||
trace!(target: "poa", "Multiple blocks proposed for step {}.", step);
|
||||
trace!(target: "engine", "Multiple blocks proposed for step {}.", step);
|
||||
self.validators.report_malicious(header.author());
|
||||
Err(EngineError::DoubleVote(header.author().clone()))?;
|
||||
}
|
||||
|
||||
@@ -159,13 +159,13 @@ impl Tendermint {
|
||||
let message = ConsensusMessage::new(signature, h, r, *s, block_hash);
|
||||
let validator = self.signer.address();
|
||||
self.votes.vote(message.clone(), &validator);
|
||||
debug!(target: "poa", "Generated {:?} as {}.", message, validator);
|
||||
debug!(target: "engine", "Generated {:?} as {}.", message, validator);
|
||||
self.handle_valid_message(&message);
|
||||
|
||||
Some(message_rlp)
|
||||
},
|
||||
Err(e) => {
|
||||
trace!(target: "poa", "Could not sign the message {}", e);
|
||||
trace!(target: "engine", "Could not sign the message {}", e);
|
||||
None
|
||||
},
|
||||
}
|
||||
@@ -186,7 +186,7 @@ impl Tendermint {
|
||||
|
||||
fn to_next_height(&self, height: Height) {
|
||||
let new_height = height + 1;
|
||||
debug!(target: "poa", "Received a Commit, transitioning to height {}.", new_height);
|
||||
debug!(target: "engine", "Received a Commit, transitioning to height {}.", new_height);
|
||||
self.last_lock.store(0, AtomicOrdering::SeqCst);
|
||||
self.height.store(new_height, AtomicOrdering::SeqCst);
|
||||
self.view.store(0, AtomicOrdering::SeqCst);
|
||||
@@ -196,7 +196,7 @@ impl Tendermint {
|
||||
/// Use via step_service to transition steps.
|
||||
fn to_step(&self, step: Step) {
|
||||
if let Err(io_err) = self.step_service.send_message(step) {
|
||||
warn!(target: "poa", "Could not proceed to step {}.", io_err)
|
||||
warn!(target: "engine", "Could not proceed to step {}.", io_err)
|
||||
}
|
||||
*self.step.write() = step;
|
||||
match step {
|
||||
@@ -212,10 +212,10 @@ impl Tendermint {
|
||||
self.generate_and_broadcast_message(block_hash);
|
||||
},
|
||||
Step::Precommit => {
|
||||
trace!(target: "poa", "to_step: Precommit.");
|
||||
trace!(target: "engine", "to_step: Precommit.");
|
||||
let block_hash = match *self.lock_change.read() {
|
||||
Some(ref m) if self.is_view(m) && m.block_hash.is_some() => {
|
||||
trace!(target: "poa", "Setting last lock: {}", m.vote_step.view);
|
||||
trace!(target: "engine", "Setting last lock: {}", m.vote_step.view);
|
||||
self.last_lock.store(m.vote_step.view, AtomicOrdering::SeqCst);
|
||||
m.block_hash
|
||||
},
|
||||
@@ -224,7 +224,7 @@ impl Tendermint {
|
||||
self.generate_and_broadcast_message(block_hash);
|
||||
},
|
||||
Step::Commit => {
|
||||
trace!(target: "poa", "to_step: Commit.");
|
||||
trace!(target: "engine", "to_step: Commit.");
|
||||
// Commit the block using a complete signature set.
|
||||
let view = self.view.load(AtomicOrdering::SeqCst);
|
||||
let height = self.height.load(AtomicOrdering::SeqCst);
|
||||
@@ -234,7 +234,7 @@ impl Tendermint {
|
||||
let proposal_step = VoteStep::new(height, view, Step::Propose);
|
||||
let precommit_step = VoteStep::new(proposal_step.height, proposal_step.view, Step::Precommit);
|
||||
if let Some(seal) = self.votes.seal_signatures(proposal_step, precommit_step, &block_hash) {
|
||||
trace!(target: "poa", "Collected seal: {:?}", seal);
|
||||
trace!(target: "engine", "Collected seal: {:?}", seal);
|
||||
let seal = vec![
|
||||
::rlp::encode(&view).to_vec(),
|
||||
::rlp::encode(&seal.proposal).to_vec(),
|
||||
@@ -243,7 +243,7 @@ impl Tendermint {
|
||||
self.submit_seal(block_hash, seal);
|
||||
self.to_next_height(height);
|
||||
} else {
|
||||
warn!(target: "poa", "Not enough votes found!");
|
||||
warn!(target: "engine", "Not enough votes found!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -262,7 +262,7 @@ impl Tendermint {
|
||||
/// Find the designated for the given view.
|
||||
fn view_proposer(&self, height: Height, view: View) -> Address {
|
||||
let proposer_nonce = height + view;
|
||||
trace!(target: "poa", "Proposer nonce: {}", proposer_nonce);
|
||||
trace!(target: "engine", "Proposer nonce: {}", proposer_nonce);
|
||||
self.validators.get(proposer_nonce)
|
||||
}
|
||||
|
||||
@@ -291,7 +291,7 @@ impl Tendermint {
|
||||
}
|
||||
|
||||
fn increment_view(&self, n: View) {
|
||||
trace!(target: "poa", "increment_view: New view.");
|
||||
trace!(target: "engine", "increment_view: New view.");
|
||||
self.view.fetch_add(n, AtomicOrdering::SeqCst);
|
||||
}
|
||||
|
||||
@@ -331,7 +331,7 @@ impl Tendermint {
|
||||
&& message.block_hash.is_some()
|
||||
&& self.has_enough_aligned_votes(message);
|
||||
if lock_change {
|
||||
trace!(target: "poa", "handle_valid_message: Lock change.");
|
||||
trace!(target: "engine", "handle_valid_message: Lock change.");
|
||||
*self.lock_change.write() = Some(message.clone());
|
||||
}
|
||||
// Check if it can affect the step transition.
|
||||
@@ -349,7 +349,7 @@ impl Tendermint {
|
||||
self.increment_view(vote_step.view - self.view.load(AtomicOrdering::SeqCst));
|
||||
Some(Step::Precommit)
|
||||
},
|
||||
// Avoid counting twice.
|
||||
// Avoid counting votes twice.
|
||||
Step::Prevote if lock_change => Some(Step::Precommit),
|
||||
Step::Prevote if self.has_enough_aligned_votes(message) => Some(Step::Precommit),
|
||||
Step::Prevote if self.has_enough_future_step_votes(&vote_step) => {
|
||||
@@ -360,7 +360,7 @@ impl Tendermint {
|
||||
};
|
||||
|
||||
if let Some(step) = next_step {
|
||||
trace!(target: "poa", "Transition to {:?} triggered.", step);
|
||||
trace!(target: "engine", "Transition to {:?} triggered.", step);
|
||||
self.to_step(step);
|
||||
}
|
||||
}
|
||||
@@ -429,7 +429,7 @@ impl Engine for Tendermint {
|
||||
let vote_info = message_info_rlp(&VoteStep::new(height, view, Step::Propose), bh.clone());
|
||||
if let Ok(signature) = self.signer.sign(vote_info.sha3()).map(Into::into) {
|
||||
// Insert Propose vote.
|
||||
debug!(target: "poa", "Submitting proposal {} at height {} view {}.", header.bare_hash(), height, view);
|
||||
debug!(target: "engine", "Submitting proposal {} at height {} view {}.", header.bare_hash(), height, view);
|
||||
self.votes.vote(ConsensusMessage::new(signature, height, view, Step::Propose, bh), author);
|
||||
// Remember proposal for later seal submission.
|
||||
*self.proposal.write() = bh;
|
||||
@@ -439,7 +439,7 @@ impl Engine for Tendermint {
|
||||
::rlp::EMPTY_LIST_RLP.to_vec()
|
||||
])
|
||||
} else {
|
||||
warn!(target: "poa", "generate_seal: FAIL: accounts secret key unavailable");
|
||||
warn!(target: "engine", "generate_seal: FAIL: accounts secret key unavailable");
|
||||
Seal::None
|
||||
}
|
||||
}
|
||||
@@ -457,7 +457,7 @@ impl Engine for Tendermint {
|
||||
self.validators.report_malicious(&sender);
|
||||
Err(EngineError::DoubleVote(sender))?
|
||||
}
|
||||
trace!(target: "poa", "Handling a valid {:?} from {}.", message, sender);
|
||||
trace!(target: "engine", "Handling a valid {:?} from {}.", message, sender);
|
||||
self.handle_valid_message(&message);
|
||||
}
|
||||
Ok(())
|
||||
@@ -519,7 +519,7 @@ impl Engine for Tendermint {
|
||||
if origins.insert(address) {
|
||||
signature_count += 1;
|
||||
} else {
|
||||
warn!(target: "poa", "verify_block_unordered: Duplicate signature from {} on the seal.", address);
|
||||
warn!(target: "engine", "verify_block_unordered: Duplicate signature from {} on the seal.", address);
|
||||
Err(BlockError::InvalidSeal)?;
|
||||
}
|
||||
}
|
||||
@@ -577,12 +577,12 @@ impl Engine for Tendermint {
|
||||
let proposal = ConsensusMessage::new_proposal(header).expect("block went through full verification; this Engine verifies new_proposal creation; qed");
|
||||
if signatures_len != 1 {
|
||||
// New Commit received, skip to next height.
|
||||
trace!(target: "poa", "Received a commit: {:?}.", proposal.vote_step);
|
||||
trace!(target: "engine", "Received a commit: {:?}.", proposal.vote_step);
|
||||
self.to_next_height(proposal.vote_step.height);
|
||||
return false;
|
||||
}
|
||||
let proposer = proposal.verify().expect("block went through full verification; this Engine tries verify; qed");
|
||||
debug!(target: "poa", "Received a new proposal {:?} from {}.", proposal.vote_step, proposer);
|
||||
debug!(target: "engine", "Received a new proposal {:?} from {}.", proposal.vote_step, proposer);
|
||||
if self.is_view(&proposal) {
|
||||
*self.proposal.write() = proposal.block_hash.clone();
|
||||
}
|
||||
@@ -594,7 +594,7 @@ impl Engine for Tendermint {
|
||||
fn step(&self) {
|
||||
let next_step = match *self.step.read() {
|
||||
Step::Propose => {
|
||||
trace!(target: "poa", "Propose timeout.");
|
||||
trace!(target: "engine", "Propose timeout.");
|
||||
if self.proposal.read().is_none() {
|
||||
// Report the proposer if no proposal was received.
|
||||
let current_proposer = self.view_proposer(self.height.load(AtomicOrdering::SeqCst), self.view.load(AtomicOrdering::SeqCst));
|
||||
@@ -603,26 +603,26 @@ impl Engine for Tendermint {
|
||||
Step::Prevote
|
||||
},
|
||||
Step::Prevote if self.has_enough_any_votes() => {
|
||||
trace!(target: "poa", "Prevote timeout.");
|
||||
trace!(target: "engine", "Prevote timeout.");
|
||||
Step::Precommit
|
||||
},
|
||||
Step::Prevote => {
|
||||
trace!(target: "poa", "Prevote timeout without enough votes.");
|
||||
trace!(target: "engine", "Prevote timeout without enough votes.");
|
||||
self.broadcast_old_messages();
|
||||
Step::Prevote
|
||||
},
|
||||
Step::Precommit if self.has_enough_any_votes() => {
|
||||
trace!(target: "poa", "Precommit timeout.");
|
||||
trace!(target: "engine", "Precommit timeout.");
|
||||
self.increment_view(1);
|
||||
Step::Propose
|
||||
},
|
||||
Step::Precommit => {
|
||||
trace!(target: "poa", "Precommit timeout without enough votes.");
|
||||
trace!(target: "engine", "Precommit timeout without enough votes.");
|
||||
self.broadcast_old_messages();
|
||||
Step::Precommit
|
||||
},
|
||||
Step::Commit => {
|
||||
trace!(target: "poa", "Commit timeout.");
|
||||
trace!(target: "engine", "Commit timeout.");
|
||||
Step::Propose
|
||||
},
|
||||
};
|
||||
@@ -838,7 +838,6 @@ mod tests {
|
||||
|
||||
let (b, seal) = propose_default(&spec, proposer);
|
||||
assert!(b.lock().try_seal(spec.engine.as_ref(), seal).is_ok());
|
||||
spec.engine.stop();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -850,7 +849,6 @@ mod tests {
|
||||
let (b, seal) = propose_default(&spec, proposer);
|
||||
let sealed = b.lock().seal(spec.engine.as_ref(), seal).unwrap();
|
||||
assert!(spec.engine.is_proposal(sealed.header()));
|
||||
spec.engine.stop();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -858,7 +856,7 @@ mod tests {
|
||||
let (spec, tap) = setup();
|
||||
let engine = spec.engine.clone();
|
||||
|
||||
let v0 = insert_and_register(&tap, engine.as_ref(), "0");
|
||||
let v0 = insert_and_unlock(&tap, "0");
|
||||
let v1 = insert_and_register(&tap, engine.as_ref(), "1");
|
||||
|
||||
let h = 1;
|
||||
@@ -883,7 +881,6 @@ mod tests {
|
||||
assert!(notify.messages.read().contains(&prevote_current));
|
||||
assert!(notify.messages.read().contains(&precommit_current));
|
||||
assert!(notify.messages.read().contains(&prevote_future));
|
||||
engine.stop();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -933,7 +930,5 @@ mod tests {
|
||||
// Last precommit.
|
||||
vote(engine, |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Precommit, proposal);
|
||||
assert_eq!(client.chain_info().best_block_number, 1);
|
||||
|
||||
engine.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,9 @@ fn set_timeout<S: Sync + Send + Clone>(io: &IoContext<S>, timeout: Duration) {
|
||||
|
||||
impl <S> IoHandler<S> for TransitionHandler<S> where S: Sync + Send + Clone + 'static {
|
||||
fn initialize(&self, io: &IoContext<S>) {
|
||||
set_timeout(io, self.timeouts.initial());
|
||||
let initial = self.timeouts.initial();
|
||||
trace!(target: "engine", "Setting the initial timeout to {}.", initial);
|
||||
set_timeout(io, initial);
|
||||
}
|
||||
|
||||
/// Call step after timeout.
|
||||
|
||||
@@ -118,13 +118,13 @@ impl <M: Message + Default + Encodable + Debug> VoteCollector<M> {
|
||||
.get(&message.round())
|
||||
.map_or(false, |c| {
|
||||
let is_known = c.messages.contains(message);
|
||||
if is_known { trace!(target: "poa", "Known message: {:?}.", message); }
|
||||
if is_known { trace!(target: "engine", "Known message: {:?}.", message); }
|
||||
is_known
|
||||
})
|
||||
|| {
|
||||
let guard = self.votes.read();
|
||||
let is_old = guard.keys().next().map_or(true, |oldest| message.round() <= oldest);
|
||||
if is_old { trace!(target: "poa", "Old message {:?}.", message); }
|
||||
if is_old { trace!(target: "engine", "Old message {:?}.", message); }
|
||||
is_old
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<UnverifiedTransaction>,
|
||||
default_origin: TransactionOrigin,
|
||||
min_block: Option<BlockNumber>,
|
||||
condition: Option<TransactionCondition>,
|
||||
transaction_queue: &mut BanningTransactionQueue,
|
||||
) -> Vec<Result<TransactionImportResult, Error>> {
|
||||
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<PendingTransaction> {
|
||||
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> {
|
||||
@@ -942,14 +942,14 @@ impl MinerService for Miner {
|
||||
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();
|
||||
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));
|
||||
|
||||
@@ -154,7 +154,7 @@ pub trait MinerService : Send + Sync {
|
||||
fn pending_transactions(&self) -> Vec<PendingTransaction>;
|
||||
|
||||
/// 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.
|
||||
fn future_transactions(&self) -> Vec<PendingTransaction>;
|
||||
|
||||
@@ -276,17 +276,17 @@ struct VerifiedTransaction {
|
||||
origin: TransactionOrigin,
|
||||
/// Insertion time
|
||||
insertion_time: QueuingInstant,
|
||||
/// Delay until specifid block.
|
||||
min_block: Option<BlockNumber>,
|
||||
/// Delay until specified condition is met.
|
||||
condition: Option<Condition>,
|
||||
}
|
||||
|
||||
impl VerifiedTransaction {
|
||||
fn new(transaction: SignedTransaction, origin: TransactionOrigin, time: QueuingInstant, min_block: Option<BlockNumber>) -> Self {
|
||||
fn new(transaction: SignedTransaction, origin: TransactionOrigin, time: QueuingInstant, condition: Option<Condition>) -> 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<BlockNumber>,
|
||||
condition: Option<Condition>,
|
||||
details_provider: &TransactionDetailsProvider,
|
||||
) -> Result<TransactionImportResult, Error> {
|
||||
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<BlockNumber>,
|
||||
condition: Option<Condition>,
|
||||
details_provider: &TransactionDetailsProvider,
|
||||
) -> Result<TransactionImportResult, Error> {
|
||||
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<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) {
|
||||
|
||||
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<SignedTransaction> {
|
||||
pub fn top_transactions_at(&self, best_block: BlockNumber, best_timestamp: u64) -> Vec<SignedTransaction> {
|
||||
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<PendingTransaction> {
|
||||
pub fn pending_transactions(&self, best_block: BlockNumber, best_timestamp: u64) -> Vec<PendingTransaction> {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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<H256>,
|
||||
/// Best ancient block number.
|
||||
|
||||
@@ -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<BlockNumber>,
|
||||
/// To be activated at this condition. `None` for immediately.
|
||||
pub condition: Option<Condition>,
|
||||
}
|
||||
|
||||
impl PendingTransaction {
|
||||
/// 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 {
|
||||
transaction: signed,
|
||||
min_block: min_block,
|
||||
condition: condition,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -466,7 +476,7 @@ impl From<SignedTransaction> for PendingTransaction {
|
||||
fn from(t: SignedTransaction) -> Self {
|
||||
PendingTransaction {
|
||||
transaction: t,
|
||||
min_block: None,
|
||||
condition: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user