Integration with zgp whitelist contract (#4215)
* zgp-transactions checker * polishing * rename + refactor * refuse-service-transactions cl option * fixed tests compilation
This commit is contained in:
parent
220084d77d
commit
092e24b9f2
@ -308,22 +308,29 @@ impl TestBlockChainClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts a transaction to miners transactions queue.
|
/// Inserts a transaction with given gas price to miners transactions queue.
|
||||||
pub fn insert_transaction_to_queue(&self) {
|
pub fn insert_transaction_with_gas_price_to_queue(&self, gas_price: U256) -> H256 {
|
||||||
let keypair = Random.generate().unwrap();
|
let keypair = Random.generate().unwrap();
|
||||||
let tx = Transaction {
|
let tx = Transaction {
|
||||||
action: Action::Create,
|
action: Action::Create,
|
||||||
value: U256::from(100),
|
value: U256::from(100),
|
||||||
data: "3331600055".from_hex().unwrap(),
|
data: "3331600055".from_hex().unwrap(),
|
||||||
gas: U256::from(100_000),
|
gas: U256::from(100_000),
|
||||||
gas_price: U256::from(20_000_000_000u64),
|
gas_price: gas_price,
|
||||||
nonce: U256::zero()
|
nonce: U256::zero()
|
||||||
};
|
};
|
||||||
let signed_tx = tx.sign(keypair.secret(), None);
|
let signed_tx = tx.sign(keypair.secret(), None);
|
||||||
self.set_balance(signed_tx.sender(), U256::from(10_000_000_000_000_000_000u64));
|
self.set_balance(signed_tx.sender(), 10_000_000_000_000_000_000u64.into());
|
||||||
|
let hash = signed_tx.hash();
|
||||||
let res = self.miner.import_external_transactions(self, vec![signed_tx.into()]);
|
let res = self.miner.import_external_transactions(self, vec![signed_tx.into()]);
|
||||||
let res = res.into_iter().next().unwrap().expect("Successful import");
|
let res = res.into_iter().next().unwrap().expect("Successful import");
|
||||||
assert_eq!(res, TransactionImportResult::Current);
|
assert_eq!(res, TransactionImportResult::Current);
|
||||||
|
hash
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts a transaction to miners transactions queue.
|
||||||
|
pub fn insert_transaction_to_queue(&self) -> H256 {
|
||||||
|
self.insert_transaction_with_gas_price_to_queue(U256::from(20_000_000_000u64))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set reported history size.
|
/// Set reported history size.
|
||||||
|
@ -22,7 +22,7 @@ use std::ops::{Deref, DerefMut};
|
|||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use transaction::{SignedTransaction, Action};
|
use transaction::{SignedTransaction, Action};
|
||||||
use transient_hashmap::TransientHashMap;
|
use transient_hashmap::TransientHashMap;
|
||||||
use miner::{TransactionQueue, TransactionImportResult, TransactionOrigin, AccountDetails};
|
use miner::{TransactionQueue, TransactionQueueDetailsProvider, TransactionImportResult, TransactionOrigin};
|
||||||
use miner::transaction_queue::QueuingInstant;
|
use miner::transaction_queue::QueuingInstant;
|
||||||
use error::{Error, TransactionError};
|
use error::{Error, TransactionError};
|
||||||
use util::{Uint, U256, H256, Address, Hashable};
|
use util::{Uint, U256, H256, Address, Hashable};
|
||||||
@ -76,16 +76,12 @@ impl BanningTransactionQueue {
|
|||||||
|
|
||||||
/// Add to the queue taking bans into consideration.
|
/// Add to the queue taking bans into consideration.
|
||||||
/// May reject transaction because of the banlist.
|
/// May reject transaction because of the banlist.
|
||||||
pub fn add_with_banlist<F, G>(
|
pub fn add_with_banlist(
|
||||||
&mut self,
|
&mut self,
|
||||||
transaction: SignedTransaction,
|
transaction: SignedTransaction,
|
||||||
time: QueuingInstant,
|
time: QueuingInstant,
|
||||||
account_details: &F,
|
details_provider: &TransactionQueueDetailsProvider,
|
||||||
gas_estimator: &G,
|
) -> Result<TransactionImportResult, Error> {
|
||||||
) -> Result<TransactionImportResult, Error> where
|
|
||||||
F: Fn(&Address) -> AccountDetails,
|
|
||||||
G: Fn(&SignedTransaction) -> U256,
|
|
||||||
{
|
|
||||||
if let Threshold::BanAfter(threshold) = self.ban_threshold {
|
if let Threshold::BanAfter(threshold) = self.ban_threshold {
|
||||||
// NOTE In all checks use direct query to avoid increasing ban timeout.
|
// NOTE In all checks use direct query to avoid increasing ban timeout.
|
||||||
|
|
||||||
@ -116,7 +112,7 @@ impl BanningTransactionQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.queue.add(transaction, TransactionOrigin::External, time, None, account_details, gas_estimator)
|
self.queue.add(transaction, TransactionOrigin::External, time, None, details_provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ban transaction with given hash.
|
/// Ban transaction with given hash.
|
||||||
@ -219,22 +215,16 @@ mod tests {
|
|||||||
use transaction::{Transaction, SignedTransaction, Action};
|
use transaction::{Transaction, SignedTransaction, Action};
|
||||||
use error::{Error, TransactionError};
|
use error::{Error, TransactionError};
|
||||||
use client::TransactionImportResult;
|
use client::TransactionImportResult;
|
||||||
use miner::{TransactionQueue, TransactionOrigin, AccountDetails};
|
use miner::{TransactionQueue, TransactionOrigin};
|
||||||
use util::{Uint, U256, Address, FromHex, Hashable};
|
use util::{Uint, U256, Address, FromHex, Hashable};
|
||||||
|
use miner::transaction_queue::test::DummyTransactionDetailsProvider;
|
||||||
|
|
||||||
fn queue() -> BanningTransactionQueue {
|
fn queue() -> BanningTransactionQueue {
|
||||||
BanningTransactionQueue::new(TransactionQueue::default(), Threshold::BanAfter(1), Duration::from_secs(180))
|
BanningTransactionQueue::new(TransactionQueue::default(), Threshold::BanAfter(1), Duration::from_secs(180))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_account_details(_address: &Address) -> AccountDetails {
|
fn default_tx_provider() -> DummyTransactionDetailsProvider {
|
||||||
AccountDetails {
|
DummyTransactionDetailsProvider::default().with_account_nonce(U256::zero())
|
||||||
nonce: U256::zero(),
|
|
||||||
balance: !U256::zero(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gas_required(_tx: &SignedTransaction) -> U256 {
|
|
||||||
0.into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transaction(action: Action) -> SignedTransaction {
|
fn transaction(action: Action) -> SignedTransaction {
|
||||||
@ -264,7 +254,7 @@ mod tests {
|
|||||||
let mut txq = queue();
|
let mut txq = queue();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.queue().add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_required).unwrap();
|
txq.queue().add(tx, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
// should also deref to queue
|
// should also deref to queue
|
||||||
@ -280,12 +270,12 @@ mod tests {
|
|||||||
let banlist1 = txq.ban_sender(tx.sender());
|
let banlist1 = txq.ban_sender(tx.sender());
|
||||||
assert!(!banlist1, "Threshold not reached yet.");
|
assert!(!banlist1, "Threshold not reached yet.");
|
||||||
// Insert once
|
// Insert once
|
||||||
let import1 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required).unwrap();
|
let import1 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider()).unwrap();
|
||||||
assert_eq!(import1, TransactionImportResult::Current);
|
assert_eq!(import1, TransactionImportResult::Current);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let banlist2 = txq.ban_sender(tx.sender());
|
let banlist2 = txq.ban_sender(tx.sender());
|
||||||
let import2 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required);
|
let import2 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert!(banlist2, "Threshold should be reached - banned.");
|
assert!(banlist2, "Threshold should be reached - banned.");
|
||||||
@ -304,12 +294,12 @@ mod tests {
|
|||||||
let banlist1 = txq.ban_recipient(recipient);
|
let banlist1 = txq.ban_recipient(recipient);
|
||||||
assert!(!banlist1, "Threshold not reached yet.");
|
assert!(!banlist1, "Threshold not reached yet.");
|
||||||
// Insert once
|
// Insert once
|
||||||
let import1 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required).unwrap();
|
let import1 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider()).unwrap();
|
||||||
assert_eq!(import1, TransactionImportResult::Current);
|
assert_eq!(import1, TransactionImportResult::Current);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let banlist2 = txq.ban_recipient(recipient);
|
let banlist2 = txq.ban_recipient(recipient);
|
||||||
let import2 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required);
|
let import2 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert!(banlist2, "Threshold should be reached - banned.");
|
assert!(banlist2, "Threshold should be reached - banned.");
|
||||||
@ -326,12 +316,12 @@ mod tests {
|
|||||||
let banlist1 = txq.ban_codehash(codehash);
|
let banlist1 = txq.ban_codehash(codehash);
|
||||||
assert!(!banlist1, "Threshold not reached yet.");
|
assert!(!banlist1, "Threshold not reached yet.");
|
||||||
// Insert once
|
// Insert once
|
||||||
let import1 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required).unwrap();
|
let import1 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider()).unwrap();
|
||||||
assert_eq!(import1, TransactionImportResult::Current);
|
assert_eq!(import1, TransactionImportResult::Current);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let banlist2 = txq.ban_codehash(codehash);
|
let banlist2 = txq.ban_codehash(codehash);
|
||||||
let import2 = txq.add_with_banlist(tx.clone(), 0, &default_account_details, &gas_required);
|
let import2 = txq.add_with_banlist(tx.clone(), 0, &default_tx_provider());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert!(banlist2, "Threshold should be reached - banned.");
|
assert!(banlist2, "Threshold should be reached - banned.");
|
||||||
|
@ -29,11 +29,13 @@ use transaction::{Action, UnverifiedTransaction, PendingTransaction, SignedTrans
|
|||||||
use receipt::{Receipt, RichReceipt};
|
use receipt::{Receipt, RichReceipt};
|
||||||
use spec::Spec;
|
use spec::Spec;
|
||||||
use engines::{Engine, Seal};
|
use engines::{Engine, Seal};
|
||||||
use miner::{MinerService, MinerStatus, TransactionQueue, PrioritizationStrategy, AccountDetails, TransactionOrigin};
|
use miner::{MinerService, MinerStatus, TransactionQueue, TransactionQueueDetailsProvider, PrioritizationStrategy,
|
||||||
|
AccountDetails, TransactionOrigin};
|
||||||
use miner::banning_queue::{BanningTransactionQueue, Threshold};
|
use miner::banning_queue::{BanningTransactionQueue, Threshold};
|
||||||
use miner::work_notify::WorkPoster;
|
use miner::work_notify::WorkPoster;
|
||||||
use miner::price_info::PriceInfo;
|
use miner::price_info::PriceInfo;
|
||||||
use miner::local_transactions::{Status as LocalTransactionStatus};
|
use miner::local_transactions::{Status as LocalTransactionStatus};
|
||||||
|
use miner::service_transaction_checker::ServiceTransactionChecker;
|
||||||
use header::BlockNumber;
|
use header::BlockNumber;
|
||||||
|
|
||||||
/// Different possible definitions for pending transaction set.
|
/// Different possible definitions for pending transaction set.
|
||||||
@ -102,8 +104,10 @@ pub struct MinerOptions {
|
|||||||
pub enable_resubmission: bool,
|
pub enable_resubmission: bool,
|
||||||
/// Global gas limit for all transaction in the queue except for local and retracted.
|
/// Global gas limit for all transaction in the queue except for local and retracted.
|
||||||
pub tx_queue_gas_limit: GasLimit,
|
pub tx_queue_gas_limit: GasLimit,
|
||||||
/// Banning settings
|
/// Banning settings.
|
||||||
pub tx_queue_banning: Banning,
|
pub tx_queue_banning: Banning,
|
||||||
|
/// Do we refuse to accept service transactions even if sender is certified.
|
||||||
|
pub refuse_service_transactions: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MinerOptions {
|
impl Default for MinerOptions {
|
||||||
@ -122,6 +126,7 @@ impl Default for MinerOptions {
|
|||||||
work_queue_size: 20,
|
work_queue_size: 20,
|
||||||
enable_resubmission: true,
|
enable_resubmission: true,
|
||||||
tx_queue_banning: Banning::Disabled,
|
tx_queue_banning: Banning::Disabled,
|
||||||
|
refuse_service_transactions: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -221,6 +226,7 @@ pub struct Miner {
|
|||||||
accounts: Option<Arc<AccountProvider>>,
|
accounts: Option<Arc<AccountProvider>>,
|
||||||
work_poster: Option<WorkPoster>,
|
work_poster: Option<WorkPoster>,
|
||||||
gas_pricer: Mutex<GasPricer>,
|
gas_pricer: Mutex<GasPricer>,
|
||||||
|
service_transaction_action: ServiceTransactionAction,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Miner {
|
impl Miner {
|
||||||
@ -244,6 +250,10 @@ impl Miner {
|
|||||||
ban_duration,
|
ban_duration,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
let service_transaction_action = match options.refuse_service_transactions {
|
||||||
|
true => ServiceTransactionAction::Refuse,
|
||||||
|
false => ServiceTransactionAction::Check(ServiceTransactionChecker::default()),
|
||||||
|
};
|
||||||
Miner {
|
Miner {
|
||||||
transaction_queue: Arc::new(Mutex::new(txq)),
|
transaction_queue: Arc::new(Mutex::new(txq)),
|
||||||
next_allowed_reseal: Mutex::new(Instant::now()),
|
next_allowed_reseal: Mutex::new(Instant::now()),
|
||||||
@ -263,6 +273,7 @@ impl Miner {
|
|||||||
engine: spec.engine.clone(),
|
engine: spec.engine.clone(),
|
||||||
work_poster: work_poster,
|
work_poster: work_poster,
|
||||||
gas_pricer: Mutex::new(gas_pricer),
|
gas_pricer: Mutex::new(gas_pricer),
|
||||||
|
service_transaction_action: service_transaction_action,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -526,8 +537,8 @@ impl Miner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_gas_limit(&self, chain: &MiningBlockChainClient) {
|
fn update_gas_limit(&self, client: &MiningBlockChainClient) {
|
||||||
let gas_limit = chain.best_block_header().gas_limit();
|
let gas_limit = client.best_block_header().gas_limit();
|
||||||
let mut queue = self.transaction_queue.lock();
|
let mut queue = self.transaction_queue.lock();
|
||||||
queue.set_gas_limit(gas_limit);
|
queue.set_gas_limit(gas_limit);
|
||||||
if let GasLimit::Auto = self.options.tx_queue_gas_limit {
|
if let GasLimit::Auto = self.options.tx_queue_gas_limit {
|
||||||
@ -537,7 +548,7 @@ impl Miner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if we had to prepare new pending block.
|
/// Returns true if we had to prepare new pending block.
|
||||||
fn prepare_work_sealing(&self, chain: &MiningBlockChainClient) -> bool {
|
fn prepare_work_sealing(&self, client: &MiningBlockChainClient) -> bool {
|
||||||
trace!(target: "miner", "prepare_work_sealing: entering");
|
trace!(target: "miner", "prepare_work_sealing: entering");
|
||||||
let prepare_new = {
|
let prepare_new = {
|
||||||
let mut sealing_work = self.sealing_work.lock();
|
let mut sealing_work = self.sealing_work.lock();
|
||||||
@ -555,11 +566,11 @@ impl Miner {
|
|||||||
// | NOTE Code below requires transaction_queue and sealing_work locks. |
|
// | NOTE Code below requires transaction_queue and sealing_work locks. |
|
||||||
// | Make sure to release the locks before calling that method. |
|
// | Make sure to release the locks before calling that method. |
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
let (block, original_work_hash) = self.prepare_block(chain);
|
let (block, original_work_hash) = self.prepare_block(client);
|
||||||
self.prepare_work(block, original_work_hash);
|
self.prepare_work(block, original_work_hash);
|
||||||
}
|
}
|
||||||
let mut sealing_block_last_request = self.sealing_block_last_request.lock();
|
let mut sealing_block_last_request = self.sealing_block_last_request.lock();
|
||||||
let best_number = chain.chain_info().best_block_number;
|
let best_number = client.chain_info().best_block_number;
|
||||||
if *sealing_block_last_request != best_number {
|
if *sealing_block_last_request != best_number {
|
||||||
trace!(target: "miner", "prepare_work_sealing: Miner received request (was {}, now {}) - waking up.", *sealing_block_last_request, best_number);
|
trace!(target: "miner", "prepare_work_sealing: Miner received request (was {}, now {}) - waking up.", *sealing_block_last_request, best_number);
|
||||||
*sealing_block_last_request = best_number;
|
*sealing_block_last_request = best_number;
|
||||||
@ -571,31 +582,23 @@ impl Miner {
|
|||||||
|
|
||||||
fn add_transactions_to_queue(
|
fn add_transactions_to_queue(
|
||||||
&self,
|
&self,
|
||||||
chain: &MiningBlockChainClient,
|
client: &MiningBlockChainClient,
|
||||||
transactions: Vec<UnverifiedTransaction>,
|
transactions: Vec<UnverifiedTransaction>,
|
||||||
default_origin: TransactionOrigin,
|
default_origin: TransactionOrigin,
|
||||||
min_block: Option<BlockNumber>,
|
min_block: Option<BlockNumber>,
|
||||||
transaction_queue: &mut BanningTransactionQueue,
|
transaction_queue: &mut BanningTransactionQueue,
|
||||||
) -> Vec<Result<TransactionImportResult, Error>> {
|
) -> Vec<Result<TransactionImportResult, Error>> {
|
||||||
|
|
||||||
let fetch_account = |a: &Address| AccountDetails {
|
|
||||||
nonce: chain.latest_nonce(a),
|
|
||||||
balance: chain.latest_balance(a),
|
|
||||||
};
|
|
||||||
|
|
||||||
let accounts = self.accounts.as_ref()
|
let accounts = self.accounts.as_ref()
|
||||||
.and_then(|provider| provider.accounts().ok())
|
.and_then(|provider| provider.accounts().ok())
|
||||||
.map(|accounts| accounts.into_iter().collect::<HashSet<_>>());
|
.map(|accounts| accounts.into_iter().collect::<HashSet<_>>());
|
||||||
|
|
||||||
let schedule = chain.latest_schedule();
|
let best_block_header = client.best_block_header().decode();
|
||||||
let gas_required = |tx: &SignedTransaction| tx.gas_required(&schedule).into();
|
let insertion_time = client.chain_info().best_block_number;
|
||||||
let best_block_header = chain.best_block_header().decode();
|
|
||||||
let insertion_time = chain.chain_info().best_block_number;
|
|
||||||
|
|
||||||
transactions.into_iter()
|
transactions.into_iter()
|
||||||
.map(|tx| {
|
.map(|tx| {
|
||||||
let hash = tx.hash();
|
let hash = tx.hash();
|
||||||
if chain.transaction_block(TransactionId::Hash(hash)).is_some() {
|
if client.transaction_block(TransactionId::Hash(hash)).is_some() {
|
||||||
debug!(target: "miner", "Rejected tx {:?}: already in the blockchain", hash);
|
debug!(target: "miner", "Rejected tx {:?}: already in the blockchain", hash);
|
||||||
return Err(Error::Transaction(TransactionError::AlreadyImported));
|
return Err(Error::Transaction(TransactionError::AlreadyImported));
|
||||||
}
|
}
|
||||||
@ -614,13 +617,17 @@ impl Miner {
|
|||||||
}
|
}
|
||||||
}).unwrap_or(default_origin);
|
}).unwrap_or(default_origin);
|
||||||
|
|
||||||
|
// try to install service transaction checker before appending transactions
|
||||||
|
self.service_transaction_action.update_from_chain_client(client);
|
||||||
|
|
||||||
|
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(transaction, origin, insertion_time, min_block, &fetch_account, &gas_required)
|
transaction_queue.add(transaction, origin, insertion_time, min_block, &details_provider)
|
||||||
},
|
},
|
||||||
TransactionOrigin::External => {
|
TransactionOrigin::External => {
|
||||||
transaction_queue.add_with_banlist(transaction, insertion_time, &fetch_account, &gas_required)
|
transaction_queue.add_with_banlist(transaction, insertion_time, &details_provider)
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1158,6 +1165,60 @@ impl MinerService for Miner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Action when service transaction is received
|
||||||
|
enum ServiceTransactionAction {
|
||||||
|
/// Refuse service transaction immediately
|
||||||
|
Refuse,
|
||||||
|
/// Accept if sender is certified to send service transactions
|
||||||
|
Check(ServiceTransactionChecker),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServiceTransactionAction {
|
||||||
|
pub fn update_from_chain_client(&self, client: &MiningBlockChainClient) {
|
||||||
|
if let ServiceTransactionAction::Check(ref checker) = *self {
|
||||||
|
checker.update_from_chain_client(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check(&self, client: &MiningBlockChainClient, tx: &SignedTransaction) -> Result<bool, String> {
|
||||||
|
match *self {
|
||||||
|
ServiceTransactionAction::Refuse => Err("configured to refuse service transactions".to_owned()),
|
||||||
|
ServiceTransactionAction::Check(ref checker) => checker.check(client, tx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TransactionDetailsProvider<'a> {
|
||||||
|
client: &'a MiningBlockChainClient,
|
||||||
|
service_transaction_action: &'a ServiceTransactionAction,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TransactionDetailsProvider<'a> {
|
||||||
|
pub fn new(client: &'a MiningBlockChainClient, service_transaction_action: &'a ServiceTransactionAction) -> Self {
|
||||||
|
TransactionDetailsProvider {
|
||||||
|
client: client,
|
||||||
|
service_transaction_action: service_transaction_action,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TransactionQueueDetailsProvider for TransactionDetailsProvider<'a> {
|
||||||
|
fn fetch_account(&self, address: &Address) -> AccountDetails {
|
||||||
|
AccountDetails {
|
||||||
|
nonce: self.client.latest_nonce(address),
|
||||||
|
balance: self.client.latest_balance(address),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn estimate_gas_required(&self, tx: &SignedTransaction) -> U256 {
|
||||||
|
tx.gas_required(&self.client.latest_schedule()).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_service_transaction_acceptable(&self, tx: &SignedTransaction) -> Result<bool, String> {
|
||||||
|
self.service_transaction_action.check(self.client, tx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
@ -1222,6 +1283,7 @@ mod tests {
|
|||||||
work_queue_size: 5,
|
work_queue_size: 5,
|
||||||
enable_resubmission: true,
|
enable_resubmission: true,
|
||||||
tx_queue_banning: Banning::Disabled,
|
tx_queue_banning: Banning::Disabled,
|
||||||
|
refuse_service_transactions: false,
|
||||||
},
|
},
|
||||||
GasPricer::new_fixed(0u64.into()),
|
GasPricer::new_fixed(0u64.into()),
|
||||||
&Spec::new_test(),
|
&Spec::new_test(),
|
||||||
|
@ -46,12 +46,14 @@ mod external;
|
|||||||
mod local_transactions;
|
mod local_transactions;
|
||||||
mod miner;
|
mod miner;
|
||||||
mod price_info;
|
mod price_info;
|
||||||
|
mod service_transaction_checker;
|
||||||
mod transaction_queue;
|
mod transaction_queue;
|
||||||
mod work_notify;
|
mod work_notify;
|
||||||
|
|
||||||
pub use self::external::{ExternalMiner, ExternalMinerService};
|
pub use self::external::{ExternalMiner, ExternalMinerService};
|
||||||
pub use self::miner::{Miner, MinerOptions, Banning, PendingSet, GasPricer, GasPriceCalibratorOptions, GasLimit};
|
pub use self::miner::{Miner, MinerOptions, Banning, PendingSet, GasPricer, GasPriceCalibratorOptions, GasLimit};
|
||||||
pub use self::transaction_queue::{TransactionQueue, PrioritizationStrategy, AccountDetails, TransactionOrigin};
|
pub use self::transaction_queue::{TransactionQueue, TransactionDetailsProvider as TransactionQueueDetailsProvider,
|
||||||
|
PrioritizationStrategy, AccountDetails, TransactionOrigin};
|
||||||
pub use self::local_transactions::{Status as LocalTransactionStatus};
|
pub use self::local_transactions::{Status as LocalTransactionStatus};
|
||||||
pub use client::TransactionImportResult;
|
pub use client::TransactionImportResult;
|
||||||
|
|
||||||
|
212
ethcore/src/miner/service_transaction_checker.rs
Normal file
212
ethcore/src/miner/service_transaction_checker.rs
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
// Copyright 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 client::MiningBlockChainClient;
|
||||||
|
use transaction::SignedTransaction;
|
||||||
|
use util::{U256, Uint, Mutex};
|
||||||
|
|
||||||
|
const SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME: &'static str = "service_transaction_checker";
|
||||||
|
|
||||||
|
/// Service transactions checker.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ServiceTransactionChecker {
|
||||||
|
contract: Mutex<Option<provider::Contract>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServiceTransactionChecker {
|
||||||
|
/// Try to create instance, reading contract address from given chain client.
|
||||||
|
pub fn update_from_chain_client(&self, client: &MiningBlockChainClient) {
|
||||||
|
let mut contract = self.contract.lock();
|
||||||
|
if contract.is_none() {
|
||||||
|
*contract = client.registry_address(SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME.to_owned())
|
||||||
|
.and_then(|contract_addr| {
|
||||||
|
trace!(target: "txqueue", "Configuring for service transaction checker contract from {}", contract_addr);
|
||||||
|
|
||||||
|
Some(provider::Contract::new(contract_addr))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if service transaction can be appended to the transaction queue.
|
||||||
|
pub fn check(&self, client: &MiningBlockChainClient, tx: &SignedTransaction) -> Result<bool, String> {
|
||||||
|
debug_assert_eq!(tx.gas_price, U256::zero());
|
||||||
|
|
||||||
|
if let Some(ref contract) = *self.contract.lock() {
|
||||||
|
let do_call = |a, d| client.call_contract(a, d);
|
||||||
|
contract.certified(&do_call, &tx.sender())
|
||||||
|
} else {
|
||||||
|
Err("contract is not configured".to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod provider {
|
||||||
|
// Autogenerated from JSON contract definition using Rust contract convertor.
|
||||||
|
// Command line: --jsonabi=SimpleCertifier.abi --explicit-do-call
|
||||||
|
#![allow(unused_imports)]
|
||||||
|
use std::string::String;
|
||||||
|
use std::result::Result;
|
||||||
|
use std::fmt;
|
||||||
|
use {util, ethabi};
|
||||||
|
use util::{FixedHash, Uint};
|
||||||
|
|
||||||
|
pub struct Contract {
|
||||||
|
contract: ethabi::Contract,
|
||||||
|
address: util::Address,
|
||||||
|
|
||||||
|
}
|
||||||
|
impl Contract {
|
||||||
|
pub fn new(address: util::Address) -> Self
|
||||||
|
{
|
||||||
|
Contract {
|
||||||
|
contract: ethabi::Contract::new(ethabi::Interface::load(b"[{\"constant\":false,\"inputs\":[{\"name\":\"_new\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_who\",\"type\":\"address\"}],\"name\":\"certify\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_who\",\"type\":\"address\"},{\"name\":\"_field\",\"type\":\"string\"}],\"name\":\"getAddress\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_who\",\"type\":\"address\"}],\"name\":\"revoke\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"delegate\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_who\",\"type\":\"address\"},{\"name\":\"_field\",\"type\":\"string\"}],\"name\":\"getUint\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_new\",\"type\":\"address\"}],\"name\":\"setDelegate\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_who\",\"type\":\"address\"}],\"name\":\"certified\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_who\",\"type\":\"address\"},{\"name\":\"_field\",\"type\":\"string\"}],\"name\":\"get\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"payable\":false,\"type\":\"function\"}]").expect("JSON is autogenerated; qed")),
|
||||||
|
address: address,
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn as_string<T: fmt::Debug>(e: T) -> String { format!("{:?}", e) }
|
||||||
|
|
||||||
|
/// Auto-generated from: `{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"}`
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn set_owner<F>(&self, do_call: &F, _new: &util::Address) -> Result<(), String>
|
||||||
|
where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send {
|
||||||
|
let call = self.contract.function("setOwner".into()).map_err(Self::as_string)?;
|
||||||
|
let data = call.encode_call(
|
||||||
|
vec![ethabi::Token::Address(_new.clone().0)]
|
||||||
|
).map_err(Self::as_string)?;
|
||||||
|
call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Auto-generated from: `{"constant":false,"inputs":[{"name":"_who","type":"address"}],"name":"certify","outputs":[],"payable":false,"type":"function"}`
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn certify<F>(&self, do_call: &F, _who: &util::Address) -> Result<(), String>
|
||||||
|
where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send {
|
||||||
|
let call = self.contract.function("certify".into()).map_err(Self::as_string)?;
|
||||||
|
let data = call.encode_call(
|
||||||
|
vec![ethabi::Token::Address(_who.clone().0)]
|
||||||
|
).map_err(Self::as_string)?;
|
||||||
|
call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Auto-generated from: `{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"getAddress","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"}`
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn get_address<F>(&self, do_call: &F, _who: &util::Address, _field: &str) -> Result<util::Address, String>
|
||||||
|
where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send {
|
||||||
|
let call = self.contract.function("getAddress".into()).map_err(Self::as_string)?;
|
||||||
|
let data = call.encode_call(
|
||||||
|
vec![ethabi::Token::Address(_who.clone().0), ethabi::Token::String(_field.to_owned())]
|
||||||
|
).map_err(Self::as_string)?;
|
||||||
|
let output = call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||||
|
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||||
|
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_address().ok_or("Invalid type returned")?; util::Address::from(r) }))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Auto-generated from: `{"constant":false,"inputs":[{"name":"_who","type":"address"}],"name":"revoke","outputs":[],"payable":false,"type":"function"}`
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn revoke<F>(&self, do_call: &F, _who: &util::Address) -> Result<(), String>
|
||||||
|
where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send {
|
||||||
|
let call = self.contract.function("revoke".into()).map_err(Self::as_string)?;
|
||||||
|
let data = call.encode_call(
|
||||||
|
vec![ethabi::Token::Address(_who.clone().0)]
|
||||||
|
).map_err(Self::as_string)?;
|
||||||
|
call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Auto-generated from: `{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"}`
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn owner<F>(&self, do_call: &F) -> Result<util::Address, String>
|
||||||
|
where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send {
|
||||||
|
let call = self.contract.function("owner".into()).map_err(Self::as_string)?;
|
||||||
|
let data = call.encode_call(
|
||||||
|
vec![]
|
||||||
|
).map_err(Self::as_string)?;
|
||||||
|
let output = call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||||
|
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||||
|
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_address().ok_or("Invalid type returned")?; util::Address::from(r) }))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Auto-generated from: `{"constant":true,"inputs":[],"name":"delegate","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"}`
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn delegate<F>(&self, do_call: &F) -> Result<util::Address, String>
|
||||||
|
where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send {
|
||||||
|
let call = self.contract.function("delegate".into()).map_err(Self::as_string)?;
|
||||||
|
let data = call.encode_call(
|
||||||
|
vec![]
|
||||||
|
).map_err(Self::as_string)?;
|
||||||
|
let output = call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||||
|
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||||
|
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_address().ok_or("Invalid type returned")?; util::Address::from(r) }))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Auto-generated from: `{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"getUint","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"}`
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn get_uint<F>(&self, do_call: &F, _who: &util::Address, _field: &str) -> Result<util::U256, String>
|
||||||
|
where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send {
|
||||||
|
let call = self.contract.function("getUint".into()).map_err(Self::as_string)?;
|
||||||
|
let data = call.encode_call(
|
||||||
|
vec![ethabi::Token::Address(_who.clone().0), ethabi::Token::String(_field.to_owned())]
|
||||||
|
).map_err(Self::as_string)?;
|
||||||
|
let output = call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||||
|
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||||
|
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_uint().ok_or("Invalid type returned")?; util::U256::from(r.as_ref()) }))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Auto-generated from: `{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setDelegate","outputs":[],"payable":false,"type":"function"}`
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn set_delegate<F>(&self, do_call: &F, _new: &util::Address) -> Result<(), String>
|
||||||
|
where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send {
|
||||||
|
let call = self.contract.function("setDelegate".into()).map_err(Self::as_string)?;
|
||||||
|
let data = call.encode_call(
|
||||||
|
vec![ethabi::Token::Address(_new.clone().0)]
|
||||||
|
).map_err(Self::as_string)?;
|
||||||
|
call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Auto-generated from: `{"constant":true,"inputs":[{"name":"_who","type":"address"}],"name":"certified","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"}`
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn certified<F>(&self, do_call: &F, _who: &util::Address) -> Result<bool, String>
|
||||||
|
where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send {
|
||||||
|
let call = self.contract.function("certified".into()).map_err(Self::as_string)?;
|
||||||
|
let data = call.encode_call(
|
||||||
|
vec![ethabi::Token::Address(_who.clone().0)]
|
||||||
|
).map_err(Self::as_string)?;
|
||||||
|
let output = call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||||
|
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||||
|
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_bool().ok_or("Invalid type returned")?; r }))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Auto-generated from: `{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"get","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"}`
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn get<F>(&self, do_call: &F, _who: &util::Address, _field: &str) -> Result<util::H256, String>
|
||||||
|
where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send {
|
||||||
|
let call = self.contract.function("get".into()).map_err(Self::as_string)?;
|
||||||
|
let data = call.encode_call(
|
||||||
|
vec![ethabi::Token::Address(_who.clone().0), ethabi::Token::String(_field.to_owned())]
|
||||||
|
).map_err(Self::as_string)?;
|
||||||
|
let output = call.decode_output((do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||||
|
let mut result = output.into_iter().rev().collect::<Vec<_>>();
|
||||||
|
Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_fixed_bytes().ok_or("Invalid type returned")?; util::H256::from_slice(r.as_ref()) }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -31,10 +31,30 @@
|
|||||||
//!
|
//!
|
||||||
//! use util::{Uint, U256, Address};
|
//! use util::{Uint, U256, Address};
|
||||||
//! use ethkey::{Random, Generator};
|
//! use ethkey::{Random, Generator};
|
||||||
//! use ethcore::miner::{TransactionQueue, AccountDetails, TransactionOrigin};
|
//! use ethcore::miner::{TransactionQueue, TransactionQueueDetailsProvider, AccountDetails, TransactionOrigin};
|
||||||
//! use ethcore::transaction::*;
|
//! use ethcore::transaction::*;
|
||||||
//! use rustc_serialize::hex::FromHex;
|
//! use rustc_serialize::hex::FromHex;
|
||||||
//!
|
//!
|
||||||
|
//! #[derive(Default)]
|
||||||
|
//! struct DummyTransactionDetailsProvider;
|
||||||
|
//!
|
||||||
|
//! impl TransactionQueueDetailsProvider for DummyTransactionDetailsProvider {
|
||||||
|
//! fn fetch_account(&self, _address: &Address) -> AccountDetails {
|
||||||
|
//! AccountDetails {
|
||||||
|
//! nonce: U256::from(10),
|
||||||
|
//! balance: U256::from(1_000_000)
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! fn estimate_gas_required(&self, _tx: &SignedTransaction) -> U256 {
|
||||||
|
//! 2.into()
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! fn is_service_transaction_acceptable(&self, _tx: &SignedTransaction) -> Result<bool, String> {
|
||||||
|
//! Ok(true)
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
//! fn main() {
|
//! fn main() {
|
||||||
//! let key = Random.generate().unwrap();
|
//! let key = Random.generate().unwrap();
|
||||||
//! let t1 = Transaction { action: Action::Create, value: U256::from(100), data: "3331600055".from_hex().unwrap(),
|
//! let t1 = Transaction { action: Action::Create, value: U256::from(100), data: "3331600055".from_hex().unwrap(),
|
||||||
@ -44,15 +64,11 @@
|
|||||||
//!
|
//!
|
||||||
//! let st1 = t1.sign(&key.secret(), None);
|
//! let st1 = t1.sign(&key.secret(), None);
|
||||||
//! let st2 = t2.sign(&key.secret(), None);
|
//! let st2 = t2.sign(&key.secret(), None);
|
||||||
//! let default_account_details = |_a: &Address| AccountDetails {
|
//! let details_provider = DummyTransactionDetailsProvider::default();
|
||||||
//! nonce: U256::from(10),
|
|
||||||
//! balance: U256::from(1_000_000),
|
|
||||||
//! };
|
|
||||||
//! let gas_estimator = |_tx: &SignedTransaction| 2.into();
|
|
||||||
//!
|
//!
|
||||||
//! let mut txq = TransactionQueue::default();
|
//! let mut txq = TransactionQueue::default();
|
||||||
//! txq.add(st2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
//! txq.add(st2.clone(), TransactionOrigin::External, 0, None, &details_provider).unwrap();
|
||||||
//! txq.add(st1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
//! txq.add(st1.clone(), TransactionOrigin::External, 0, None, &details_provider).unwrap();
|
||||||
//!
|
//!
|
||||||
//! // Check status
|
//! // Check status
|
||||||
//! assert_eq!(txq.status().pending, 2);
|
//! assert_eq!(txq.status().pending, 2);
|
||||||
@ -498,6 +514,16 @@ pub enum PrioritizationStrategy {
|
|||||||
pub type QueuingInstant = BlockNumber;
|
pub type QueuingInstant = BlockNumber;
|
||||||
const DEFAULT_QUEUING_PERIOD: BlockNumber = 128;
|
const DEFAULT_QUEUING_PERIOD: BlockNumber = 128;
|
||||||
|
|
||||||
|
/// `TransactionQueue` transaction details provider.
|
||||||
|
pub trait TransactionDetailsProvider {
|
||||||
|
/// Fetch transaction-related account details.
|
||||||
|
fn fetch_account(&self, address: &Address) -> AccountDetails;
|
||||||
|
/// Estimate gas required for transaction.
|
||||||
|
fn estimate_gas_required(&self, tx: &SignedTransaction) -> U256;
|
||||||
|
/// Check if this service transaction can be accepted by `TransactionQueue`.
|
||||||
|
fn is_service_transaction_acceptable(&self, tx: &SignedTransaction) -> Result<bool, String>;
|
||||||
|
}
|
||||||
|
|
||||||
/// `TransactionQueue` implementation
|
/// `TransactionQueue` implementation
|
||||||
pub struct TransactionQueue {
|
pub struct TransactionQueue {
|
||||||
/// Prioritization strategy for this queue
|
/// Prioritization strategy for this queue
|
||||||
@ -633,25 +659,21 @@ impl TransactionQueue {
|
|||||||
|
|
||||||
/// Add signed transaction to queue to be verified and imported.
|
/// Add signed transaction to queue to be verified and imported.
|
||||||
///
|
///
|
||||||
/// NOTE fetch_account and gas_estimator should be cheap to compute
|
/// NOTE details_provider methods should be cheap to compute
|
||||||
/// otherwise it might open up an attack vector.
|
/// otherwise it might open up an attack vector.
|
||||||
pub fn add<F, G>(
|
pub fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
tx: SignedTransaction,
|
tx: SignedTransaction,
|
||||||
origin: TransactionOrigin,
|
origin: TransactionOrigin,
|
||||||
time: QueuingInstant,
|
time: QueuingInstant,
|
||||||
min_block: Option<BlockNumber>,
|
min_block: Option<BlockNumber>,
|
||||||
fetch_account: &F,
|
details_provider: &TransactionDetailsProvider,
|
||||||
gas_estimator: &G,
|
) -> Result<TransactionImportResult, Error> {
|
||||||
) -> Result<TransactionImportResult, Error> where
|
|
||||||
F: Fn(&Address) -> AccountDetails,
|
|
||||||
G: Fn(&SignedTransaction) -> U256,
|
|
||||||
{
|
|
||||||
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, fetch_account, gas_estimator);
|
let result = self.add_internal(tx, origin, time, min_block, details_provider);
|
||||||
match result {
|
match result {
|
||||||
Ok(TransactionImportResult::Current) => {
|
Ok(TransactionImportResult::Current) => {
|
||||||
self.local_transactions.mark_pending(hash);
|
self.local_transactions.mark_pending(hash);
|
||||||
@ -672,25 +694,23 @@ impl TransactionQueue {
|
|||||||
}
|
}
|
||||||
result
|
result
|
||||||
} else {
|
} else {
|
||||||
self.add_internal(tx, origin, time, min_block, fetch_account, gas_estimator)
|
self.add_internal(tx, origin, time, min_block, details_provider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds signed transaction to the queue.
|
/// Adds signed transaction to the queue.
|
||||||
fn add_internal<F, G>(
|
fn add_internal(
|
||||||
&mut self,
|
&mut self,
|
||||||
tx: SignedTransaction,
|
tx: SignedTransaction,
|
||||||
origin: TransactionOrigin,
|
origin: TransactionOrigin,
|
||||||
time: QueuingInstant,
|
time: QueuingInstant,
|
||||||
min_block: Option<BlockNumber>,
|
min_block: Option<BlockNumber>,
|
||||||
fetch_account: &F,
|
details_provider: &TransactionDetailsProvider,
|
||||||
gas_estimator: &G,
|
) -> Result<TransactionImportResult, Error> {
|
||||||
) -> Result<TransactionImportResult, Error> where
|
if origin != TransactionOrigin::Local && tx.gas_price < self.minimal_gas_price {
|
||||||
F: Fn(&Address) -> AccountDetails,
|
// if it is non-service-transaction => drop
|
||||||
G: Fn(&SignedTransaction) -> U256,
|
let is_service_transaction = tx.gas_price.is_zero();
|
||||||
{
|
if !is_service_transaction {
|
||||||
|
|
||||||
if tx.gas_price < self.minimal_gas_price && origin != TransactionOrigin::Local {
|
|
||||||
trace!(target: "txqueue",
|
trace!(target: "txqueue",
|
||||||
"Dropping transaction below minimal gas price threshold: {:?} (gp: {} < {})",
|
"Dropping transaction below minimal gas price threshold: {:?} (gp: {} < {})",
|
||||||
tx.hash(),
|
tx.hash(),
|
||||||
@ -704,6 +724,36 @@ impl TransactionQueue {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let is_service_transaction_accepted = match details_provider.is_service_transaction_acceptable(&tx) {
|
||||||
|
Ok(true) => true,
|
||||||
|
Ok(false) => {
|
||||||
|
trace!(target: "txqueue",
|
||||||
|
"Dropping service transaction as sender is not certified to send service transactions: {:?} (sender: {:?})",
|
||||||
|
tx.hash(),
|
||||||
|
tx.sender(),
|
||||||
|
);
|
||||||
|
|
||||||
|
false
|
||||||
|
},
|
||||||
|
Err(contract_err) => {
|
||||||
|
trace!(target: "txqueue",
|
||||||
|
"Dropping service transaction as service contract returned error: {:?} (error: {:?})",
|
||||||
|
tx.hash(),
|
||||||
|
contract_err,
|
||||||
|
);
|
||||||
|
|
||||||
|
false
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if !is_service_transaction_accepted {
|
||||||
|
return Err(Error::Transaction(TransactionError::InsufficientGasPrice {
|
||||||
|
minimal: self.minimal_gas_price,
|
||||||
|
got: tx.gas_price,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let full_queues_lowest = self.effective_minimum_gas_price();
|
let full_queues_lowest = self.effective_minimum_gas_price();
|
||||||
if tx.gas_price < full_queues_lowest && origin != TransactionOrigin::Local {
|
if tx.gas_price < full_queues_lowest && origin != TransactionOrigin::Local {
|
||||||
trace!(target: "txqueue",
|
trace!(target: "txqueue",
|
||||||
@ -733,7 +783,7 @@ impl TransactionQueue {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let minimal_gas = gas_estimator(&tx);
|
let minimal_gas = details_provider.estimate_gas_required(&tx);
|
||||||
if tx.gas < minimal_gas {
|
if tx.gas < minimal_gas {
|
||||||
trace!(target: "txqueue",
|
trace!(target: "txqueue",
|
||||||
"Dropping transaction with insufficient gas: {:?} ({} > {})",
|
"Dropping transaction with insufficient gas: {:?} ({} > {})",
|
||||||
@ -748,7 +798,7 @@ impl TransactionQueue {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let client_account = 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;
|
||||||
if client_account.balance < cost {
|
if client_account.balance < cost {
|
||||||
trace!(target: "txqueue",
|
trace!(target: "txqueue",
|
||||||
@ -895,8 +945,8 @@ impl TransactionQueue {
|
|||||||
/// so transactions left in queue are processed according to client nonce.
|
/// so transactions left in queue are processed according to client nonce.
|
||||||
///
|
///
|
||||||
/// If gap is introduced marks subsequent transactions as future
|
/// If gap is introduced marks subsequent transactions as future
|
||||||
pub fn remove_invalid<T>(&mut self, transaction_hash: &H256, fetch_nonce: &T)
|
pub fn remove_invalid<F>(&mut self, transaction_hash: &H256, fetch_nonce: &F)
|
||||||
where T: Fn(&Address) -> U256 {
|
where F: Fn(&Address) -> U256 {
|
||||||
|
|
||||||
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());
|
||||||
let transaction = self.by_hash.remove(transaction_hash);
|
let transaction = self.by_hash.remove(transaction_hash);
|
||||||
@ -1322,7 +1372,7 @@ fn check_if_removed(sender: &Address, nonce: &U256, dropped: Option<HashMap<Addr
|
|||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
pub mod test {
|
||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
use util::table::*;
|
use util::table::*;
|
||||||
use util::*;
|
use util::*;
|
||||||
@ -1334,6 +1384,66 @@ mod test {
|
|||||||
use client::TransactionImportResult;
|
use client::TransactionImportResult;
|
||||||
use transaction::{SignedTransaction, Transaction, Action};
|
use transaction::{SignedTransaction, Transaction, Action};
|
||||||
|
|
||||||
|
pub struct DummyTransactionDetailsProvider {
|
||||||
|
account_details: AccountDetails,
|
||||||
|
gas_required: U256,
|
||||||
|
service_transactions_check_result: Result<bool, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DummyTransactionDetailsProvider {
|
||||||
|
fn default() -> Self {
|
||||||
|
DummyTransactionDetailsProvider {
|
||||||
|
account_details: default_account_details(),
|
||||||
|
gas_required: U256::zero(),
|
||||||
|
service_transactions_check_result: Ok(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DummyTransactionDetailsProvider {
|
||||||
|
pub fn with_account(mut self, account_details: AccountDetails) -> Self {
|
||||||
|
self.account_details = account_details;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_account_nonce(mut self, nonce: U256) -> Self {
|
||||||
|
self.account_details.nonce = nonce;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_tx_gas_required(mut self, gas_required: U256) -> Self {
|
||||||
|
self.gas_required = gas_required;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn service_transaction_checker_returns_error(mut self, error: &str) -> Self {
|
||||||
|
self.service_transactions_check_result = Err(error.to_owned());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn service_transaction_checker_accepts(mut self, accepts: bool) -> Self {
|
||||||
|
self.service_transactions_check_result = Ok(accepts);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransactionDetailsProvider for DummyTransactionDetailsProvider {
|
||||||
|
fn fetch_account(&self, _address: &Address) -> AccountDetails {
|
||||||
|
AccountDetails {
|
||||||
|
nonce: self.account_details.nonce,
|
||||||
|
balance: self.account_details.balance,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn estimate_gas_required(&self, _tx: &SignedTransaction) -> U256 {
|
||||||
|
self.gas_required
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_service_transaction_acceptable(&self, _tx: &SignedTransaction) -> Result<bool, String> {
|
||||||
|
self.service_transactions_check_result.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn unwrap_tx_err(err: Result<TransactionImportResult, Error>) -> TransactionError {
|
fn unwrap_tx_err(err: Result<TransactionImportResult, Error>) -> TransactionError {
|
||||||
match err.unwrap_err() {
|
match err.unwrap_err() {
|
||||||
Error::Transaction(e) => e,
|
Error::Transaction(e) => e,
|
||||||
@ -1370,15 +1480,19 @@ mod test {
|
|||||||
new_tx(default_nonce(), default_gas_price())
|
new_tx(default_nonce(), default_gas_price())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_account_details(_address: &Address) -> AccountDetails {
|
fn default_account_details() -> AccountDetails {
|
||||||
AccountDetails {
|
AccountDetails {
|
||||||
nonce: default_nonce(),
|
nonce: default_nonce(),
|
||||||
balance: !U256::zero()
|
balance: !U256::zero()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gas_estimator(_tx: &SignedTransaction) -> U256 {
|
fn default_account_details_for_addr(_a: &Address) -> AccountDetails {
|
||||||
U256::zero()
|
default_account_details()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_tx_provider() -> DummyTransactionDetailsProvider {
|
||||||
|
DummyTransactionDetailsProvider::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_tx_pair(nonce: U256, gas_price: U256, nonce_increment: U256, gas_price_increment: U256) -> (SignedTransaction, SignedTransaction) {
|
fn new_tx_pair(nonce: U256, gas_price: U256, nonce_increment: U256, gas_price_increment: U256) -> (SignedTransaction, SignedTransaction) {
|
||||||
@ -1432,14 +1546,14 @@ mod test {
|
|||||||
let (tx1, tx2) = new_tx_pair(123.into(), 1.into(), 1.into(), 0.into());
|
let (tx1, tx2) = new_tx_pair(123.into(), 1.into(), 1.into(), 0.into());
|
||||||
let sender = tx1.sender();
|
let sender = tx1.sender();
|
||||||
let nonce = tx1.nonce;
|
let nonce = tx1.nonce;
|
||||||
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
assert_eq!(txq.status().pending, 2);
|
assert_eq!(txq.status().pending, 2);
|
||||||
assert_eq!(txq.last_nonce(&sender), Some(nonce + 1.into()));
|
assert_eq!(txq.last_nonce(&sender), Some(nonce + 1.into()));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let tx = new_tx(123.into(), 1.into());
|
let tx = new_tx(123.into(), 1.into());
|
||||||
let res = txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
let res = txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
// No longer the case as we don't even consider a transaction that isn't above a full
|
// No longer the case as we don't even consider a transaction that isn't above a full
|
||||||
@ -1592,16 +1706,15 @@ mod test {
|
|||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::default();
|
let mut txq = TransactionQueue::default();
|
||||||
let (tx, tx2) = new_similar_tx_pair();
|
let (tx, tx2) = new_similar_tx_pair();
|
||||||
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance:
|
let prev_nonce = default_account_details().nonce - U256::one();
|
||||||
!U256::zero() };
|
|
||||||
|
|
||||||
// First insert one transaction to future
|
// First insert one transaction to future
|
||||||
let res = txq.add(tx, TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator);
|
let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce));
|
||||||
assert_eq!(res.unwrap(), TransactionImportResult::Future);
|
assert_eq!(res.unwrap(), TransactionImportResult::Future);
|
||||||
assert_eq!(txq.status().future, 1);
|
assert_eq!(txq.status().future, 1);
|
||||||
|
|
||||||
// now import second transaction to current
|
// now import second transaction to current
|
||||||
let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider());
|
||||||
|
|
||||||
// and then there should be only one transaction in current (the one with higher gas_price)
|
// and then there should be only one transaction in current (the one with higher gas_price)
|
||||||
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||||
@ -1618,16 +1731,15 @@ mod test {
|
|||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::default();
|
let mut txq = TransactionQueue::default();
|
||||||
let (tx, tx2) = new_tx_pair_default(1.into(), 1.into());
|
let (tx, tx2) = new_tx_pair_default(1.into(), 1.into());
|
||||||
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance:
|
let prev_nonce = default_account_details().nonce - U256::one();
|
||||||
!U256::zero() };
|
|
||||||
|
|
||||||
// First insert one transaction to future
|
// First insert one transaction to future
|
||||||
let res = txq.add(tx.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator);
|
let res = txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce));
|
||||||
assert_eq!(res.unwrap(), TransactionImportResult::Future);
|
assert_eq!(res.unwrap(), TransactionImportResult::Future);
|
||||||
assert_eq!(txq.status().future, 1);
|
assert_eq!(txq.status().future, 1);
|
||||||
|
|
||||||
// now import second transaction to current
|
// now import second transaction to current
|
||||||
let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||||
@ -1647,7 +1759,7 @@ mod test {
|
|||||||
let tx = new_tx_default();
|
let tx = new_tx_default();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||||
@ -1666,10 +1778,10 @@ mod test {
|
|||||||
txq.set_minimal_gas_price(15.into());
|
txq.set_minimal_gas_price(15.into());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let res1 = txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
let res1 = txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider());
|
||||||
let res2 = txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
let res2 = txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider());
|
||||||
let res3 = txq.add(tx3, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
let res3 = txq.add(tx3, TransactionOrigin::External, 0, None, &default_tx_provider());
|
||||||
let res4 = txq.add(tx4, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
let res4 = txq.add(tx4, TransactionOrigin::External, 0, None, &default_tx_provider());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(res1.unwrap(), TransactionImportResult::Current);
|
assert_eq!(res1.unwrap(), TransactionImportResult::Current);
|
||||||
@ -1700,10 +1812,10 @@ mod test {
|
|||||||
txq.set_minimal_gas_price(15.into());
|
txq.set_minimal_gas_price(15.into());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let res1 = txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
let res1 = txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider());
|
||||||
let res2 = txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
let res2 = txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider());
|
||||||
let res3 = txq.add(tx3, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
let res3 = txq.add(tx3, TransactionOrigin::External, 0, None, &default_tx_provider());
|
||||||
let res4 = txq.add(tx4, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
let res4 = txq.add(tx4, TransactionOrigin::External, 0, None, &default_tx_provider());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(res1.unwrap(), TransactionImportResult::Current);
|
assert_eq!(res1.unwrap(), TransactionImportResult::Current);
|
||||||
@ -1746,7 +1858,7 @@ mod test {
|
|||||||
txq.set_gas_limit(limit);
|
txq.set_gas_limit(limit);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(unwrap_tx_err(res), TransactionError::GasLimitExceeded {
|
assert_eq!(unwrap_tx_err(res), TransactionError::GasLimitExceeded {
|
||||||
@ -1764,13 +1876,13 @@ mod test {
|
|||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::default();
|
let mut txq = TransactionQueue::default();
|
||||||
let tx = new_tx_default();
|
let tx = new_tx_default();
|
||||||
let account = |a: &Address| AccountDetails {
|
let account = AccountDetails {
|
||||||
nonce: default_account_details(a).nonce,
|
nonce: default_account_details().nonce,
|
||||||
balance: U256::one()
|
balance: U256::one()
|
||||||
};
|
};
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let res = txq.add(tx, TransactionOrigin::External, 0, None, &account, &gas_estimator);
|
let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider().with_account(account));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(unwrap_tx_err(res), TransactionError::InsufficientBalance {
|
assert_eq!(unwrap_tx_err(res), TransactionError::InsufficientBalance {
|
||||||
@ -1790,7 +1902,7 @@ mod test {
|
|||||||
txq.set_minimal_gas_price(tx.gas_price + U256::one());
|
txq.set_minimal_gas_price(tx.gas_price + U256::one());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(unwrap_tx_err(res), TransactionError::InsufficientGasPrice {
|
assert_eq!(unwrap_tx_err(res), TransactionError::InsufficientGasPrice {
|
||||||
@ -1810,7 +1922,7 @@ mod test {
|
|||||||
txq.set_minimal_gas_price(tx.gas_price + U256::one());
|
txq.set_minimal_gas_price(tx.gas_price + U256::one());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let res = txq.add(tx, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator);
|
let res = txq.add(tx, TransactionOrigin::Local, 0, None, &default_tx_provider());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
assert_eq!(res.unwrap(), TransactionImportResult::Current);
|
||||||
@ -1827,8 +1939,8 @@ 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
|
||||||
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
let top = txq.top_transactions();
|
let top = txq.top_transactions();
|
||||||
@ -1847,9 +1959,9 @@ mod test {
|
|||||||
|
|
||||||
// when
|
// when
|
||||||
// first insert the one with higher gas price
|
// first insert the one with higher gas price
|
||||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
// then the one with lower gas price, but local
|
// then the one with lower gas price, but local
|
||||||
txq.add(tx.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
let top = txq.top_transactions();
|
let top = txq.top_transactions();
|
||||||
@ -1866,8 +1978,8 @@ mod test {
|
|||||||
// the second one has same nonce but higher `gas_price`
|
// the second one has same nonce but higher `gas_price`
|
||||||
let (_, tx0) = new_similar_tx_pair();
|
let (_, tx0) = new_similar_tx_pair();
|
||||||
|
|
||||||
txq.add(tx0.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx0.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
// the one with higher gas price is first
|
// the one with higher gas price is first
|
||||||
let top = txq.top_transactions();
|
let top = txq.top_transactions();
|
||||||
assert_eq!(top[0], tx0);
|
assert_eq!(top[0], tx0);
|
||||||
@ -1875,7 +1987,7 @@ mod test {
|
|||||||
|
|
||||||
// when
|
// when
|
||||||
// insert second as local
|
// insert second as local
|
||||||
txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
// the order should be updated
|
// the order should be updated
|
||||||
@ -1895,9 +2007,9 @@ mod test {
|
|||||||
|
|
||||||
// when
|
// when
|
||||||
// first insert local one with higher gas price
|
// first insert local one with higher gas price
|
||||||
txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap();
|
||||||
// then the one with lower gas price, but from retracted block
|
// then the one with lower gas price, but from retracted block
|
||||||
txq.add(tx.clone(), TransactionOrigin::RetractedBlock, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx.clone(), TransactionOrigin::RetractedBlock, 0, None, &default_tx_provider()).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
let top = txq.top_transactions();
|
let top = txq.top_transactions();
|
||||||
@ -1913,8 +2025,8 @@ 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
|
||||||
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
let top = txq.top_transactions();
|
let top = txq.top_transactions();
|
||||||
@ -1926,17 +2038,17 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_penalize_transactions_from_sender_in_future() {
|
fn should_penalize_transactions_from_sender_in_future() {
|
||||||
// given
|
// given
|
||||||
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance: !U256::zero() };
|
let prev_nonce = default_account_details().nonce - U256::one();
|
||||||
let mut txq = TransactionQueue::default();
|
let mut txq = TransactionQueue::default();
|
||||||
// txa, txb - slightly bigger gas price to have consistent ordering
|
// txa, txb - slightly bigger gas price to have consistent ordering
|
||||||
let (txa, txb) = new_tx_pair_default(1.into(), 0.into());
|
let (txa, txb) = new_tx_pair_default(1.into(), 0.into());
|
||||||
let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into());
|
let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into());
|
||||||
|
|
||||||
// insert everything
|
// insert everything
|
||||||
txq.add(txa.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap();
|
txq.add(txa.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap();
|
||||||
txq.add(txb.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap();
|
txq.add(txb.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap();
|
||||||
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap();
|
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap();
|
||||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap();
|
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap();
|
||||||
|
|
||||||
assert_eq!(txq.status().future, 4);
|
assert_eq!(txq.status().future, 4);
|
||||||
|
|
||||||
@ -1961,10 +2073,10 @@ mod test {
|
|||||||
let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into());
|
let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into());
|
||||||
|
|
||||||
// insert everything
|
// insert everything
|
||||||
txq.add(txa.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(txa.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(txb.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(txb.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx1.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx1.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap();
|
||||||
|
|
||||||
let top = txq.top_transactions();
|
let top = txq.top_transactions();
|
||||||
assert_eq!(top[0], tx1);
|
assert_eq!(top[0], tx1);
|
||||||
@ -1994,10 +2106,10 @@ mod test {
|
|||||||
let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into());
|
let (tx1, tx2) = new_tx_pair_with_gas_price_increment(3.into());
|
||||||
|
|
||||||
// insert everything
|
// insert everything
|
||||||
txq.add(txa.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(txa.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(txb.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(txb.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
|
|
||||||
let top = txq.top_transactions();
|
let top = txq.top_transactions();
|
||||||
assert_eq!(top[0], tx1);
|
assert_eq!(top[0], tx1);
|
||||||
@ -2026,8 +2138,8 @@ 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
|
||||||
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
let top = txq.pending_hashes();
|
let top = txq.pending_hashes();
|
||||||
@ -2044,8 +2156,8 @@ mod test {
|
|||||||
let (tx, tx2) = new_tx_pair_default(2.into(), 0.into());
|
let (tx, tx2) = new_tx_pair_default(2.into(), 0.into());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let res1 = txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
let res1 = txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
let res2 = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).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);
|
||||||
@ -2066,8 +2178,8 @@ 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_account_details, &gas_estimator).unwrap();
|
let res1 = txq.add(tx.clone(), TransactionOrigin::External, 0, Some(1), &default_tx_provider()).unwrap();
|
||||||
let res2 = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).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);
|
||||||
@ -2081,15 +2193,14 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_correctly_update_futures_when_removing() {
|
fn should_correctly_update_futures_when_removing() {
|
||||||
// given
|
// given
|
||||||
let prev_nonce = |a: &Address| AccountDetails{ nonce: default_account_details(a).nonce - U256::one(), balance:
|
let prev_nonce = default_account_details().nonce - U256::one();
|
||||||
!U256::zero() };
|
|
||||||
let next2_nonce = default_nonce() + U256::from(3);
|
let next2_nonce = default_nonce() + U256::from(3);
|
||||||
|
|
||||||
let mut txq = TransactionQueue::default();
|
let mut txq = TransactionQueue::default();
|
||||||
|
|
||||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap();
|
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap();
|
||||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap();
|
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap();
|
||||||
assert_eq!(txq.status().future, 2);
|
assert_eq!(txq.status().future, 2);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -2111,13 +2222,13 @@ mod test {
|
|||||||
let tx1 = new_unsigned_tx(124.into(), default_gas_val(), 1.into()).sign(secret, None).into();
|
let tx1 = new_unsigned_tx(124.into(), default_gas_val(), 1.into()).sign(secret, None).into();
|
||||||
let tx2 = new_unsigned_tx(125.into(), default_gas_val(), 1.into()).sign(secret, None).into();
|
let tx2 = new_unsigned_tx(125.into(), default_gas_val(), 1.into()).sign(secret, None).into();
|
||||||
|
|
||||||
txq.add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
assert_eq!(txq.status().pending, 1);
|
assert_eq!(txq.status().pending, 1);
|
||||||
txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
assert_eq!(txq.status().future, 1);
|
assert_eq!(txq.status().future, 1);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
let stats = txq.status();
|
let stats = txq.status();
|
||||||
@ -2133,8 +2244,8 @@ mod test {
|
|||||||
// given
|
// given
|
||||||
let mut txq2 = TransactionQueue::default();
|
let mut txq2 = TransactionQueue::default();
|
||||||
let (tx, tx2) = new_tx_pair_default(3.into(), 0.into());
|
let (tx, tx2) = new_tx_pair_default(3.into(), 0.into());
|
||||||
txq2.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq2.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq2.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq2.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
assert_eq!(txq2.status().pending, 1);
|
assert_eq!(txq2.status().pending, 1);
|
||||||
assert_eq!(txq2.status().future, 1);
|
assert_eq!(txq2.status().future, 1);
|
||||||
|
|
||||||
@ -2154,10 +2265,10 @@ mod test {
|
|||||||
let mut txq = TransactionQueue::default();
|
let mut txq = TransactionQueue::default();
|
||||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
let tx3 = new_tx_default();
|
let tx3 = new_tx_default();
|
||||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
assert_eq!(txq.status().future, 1);
|
assert_eq!(txq.status().future, 1);
|
||||||
txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
assert_eq!(txq.status().pending, 3);
|
assert_eq!(txq.status().pending, 3);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -2176,8 +2287,8 @@ mod test {
|
|||||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
|
|
||||||
// add
|
// add
|
||||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
let stats = txq.status();
|
let stats = txq.status();
|
||||||
assert_eq!(stats.pending, 2);
|
assert_eq!(stats.pending, 2);
|
||||||
|
|
||||||
@ -2196,11 +2307,11 @@ mod test {
|
|||||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
let sender = tx.sender();
|
let sender = tx.sender();
|
||||||
let nonce = tx.nonce;
|
let nonce = tx.nonce;
|
||||||
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
assert_eq!(txq.status().pending, 1);
|
assert_eq!(txq.status().pending, 1);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator);
|
let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
let t = txq.top_transactions();
|
let t = txq.top_transactions();
|
||||||
@ -2217,14 +2328,14 @@ mod test {
|
|||||||
txq.current.set_limit(10);
|
txq.current.set_limit(10);
|
||||||
let (tx1, tx2) = new_tx_pair_default(4.into(), 1.into());
|
let (tx1, tx2) = new_tx_pair_default(4.into(), 1.into());
|
||||||
let (tx3, tx4) = new_tx_pair_default(4.into(), 2.into());
|
let (tx3, tx4) = new_tx_pair_default(4.into(), 2.into());
|
||||||
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
assert_eq!(txq.status().pending, 2);
|
assert_eq!(txq.status().pending, 2);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
assert_eq!(txq.status().future, 1);
|
assert_eq!(txq.status().future, 1);
|
||||||
txq.add(tx4.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx4.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(txq.status().future, 1);
|
assert_eq!(txq.status().future, 1);
|
||||||
@ -2235,11 +2346,11 @@ mod test {
|
|||||||
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 100, default_gas_val() * U256::from(2), !U256::zero());
|
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 100, default_gas_val() * U256::from(2), !U256::zero());
|
||||||
let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1));
|
let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1));
|
||||||
let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2));
|
let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2));
|
||||||
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
// limited by gas
|
// limited by gas
|
||||||
txq.add(tx4.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap_err();
|
txq.add(tx4.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap_err();
|
||||||
assert_eq!(txq.status().pending, 2);
|
assert_eq!(txq.status().pending, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2249,12 +2360,12 @@ mod test {
|
|||||||
let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1));
|
let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1));
|
||||||
let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2));
|
let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2));
|
||||||
let (tx5, _) = new_tx_pair_default(U256::from(1), U256::from(2));
|
let (tx5, _) = new_tx_pair_default(U256::from(1), U256::from(2));
|
||||||
txq.add(tx1.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx1.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx2.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap();
|
||||||
// Not accepted because of limit
|
// Not accepted because of limit
|
||||||
txq.add(tx5.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap_err();
|
txq.add(tx5.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap_err();
|
||||||
txq.add(tx3.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx3.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx4.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx4.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap();
|
||||||
assert_eq!(txq.status().pending, 4);
|
assert_eq!(txq.status().pending, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2263,10 +2374,9 @@ mod test {
|
|||||||
let mut txq = TransactionQueue::default();
|
let mut txq = TransactionQueue::default();
|
||||||
let tx = new_tx_default();
|
let tx = new_tx_default();
|
||||||
let last_nonce = tx.nonce + U256::one();
|
let last_nonce = tx.nonce + U256::one();
|
||||||
let fetch_last_nonce = |_a: &Address| AccountDetails { nonce: last_nonce, balance: !U256::zero() };
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let res = txq.add(tx, TransactionOrigin::External, 0, None, &fetch_last_nonce, &gas_estimator);
|
let res = txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(last_nonce));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(unwrap_tx_err(res), TransactionError::Old);
|
assert_eq!(unwrap_tx_err(res), TransactionError::Old);
|
||||||
@ -2278,16 +2388,15 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_not_insert_same_transaction_twice() {
|
fn should_not_insert_same_transaction_twice() {
|
||||||
// given
|
// given
|
||||||
let nonce = |a: &Address| AccountDetails { nonce: default_account_details(a).nonce + U256::one(),
|
let nonce = default_account_details().nonce + U256::one();
|
||||||
balance: !U256::zero() };
|
|
||||||
let mut txq = TransactionQueue::default();
|
let mut txq = TransactionQueue::default();
|
||||||
let (_tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (_tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
assert_eq!(txq.status().future, 1);
|
assert_eq!(txq.status().future, 1);
|
||||||
assert_eq!(txq.status().pending, 0);
|
assert_eq!(txq.status().pending, 0);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &nonce, &gas_estimator);
|
let res = txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(nonce));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(unwrap_tx_err(res), TransactionError::AlreadyImported);
|
assert_eq!(unwrap_tx_err(res), TransactionError::AlreadyImported);
|
||||||
@ -2301,15 +2410,15 @@ mod test {
|
|||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::default();
|
let mut txq = TransactionQueue::default();
|
||||||
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
assert_eq!(txq.status().pending, 2);
|
assert_eq!(txq.status().pending, 2);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.remove_invalid(&tx1.hash(), &|_| default_nonce());
|
txq.remove_invalid(&tx1.hash(), &|_| default_nonce());
|
||||||
assert_eq!(txq.status().pending, 0);
|
assert_eq!(txq.status().pending, 0);
|
||||||
assert_eq!(txq.status().future, 1);
|
assert_eq!(txq.status().future, 1);
|
||||||
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
let stats = txq.status();
|
let stats = txq.status();
|
||||||
@ -2323,10 +2432,10 @@ mod test {
|
|||||||
let mut txq = TransactionQueue::default();
|
let mut txq = TransactionQueue::default();
|
||||||
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
let tx3 = new_tx_default();
|
let tx3 = new_tx_default();
|
||||||
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx2.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
assert_eq!(txq.status().future, 1);
|
assert_eq!(txq.status().future, 1);
|
||||||
txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx3.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
assert_eq!(txq.status().pending, 3);
|
assert_eq!(txq.status().pending, 3);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -2352,8 +2461,8 @@ mod test {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.add(tx, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
let stats = txq.status();
|
let stats = txq.status();
|
||||||
@ -2380,10 +2489,10 @@ mod test {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
assert_eq!(txq.status().future, 1);
|
assert_eq!(txq.status().future, 1);
|
||||||
txq.add(tx0, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx0, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
let stats = txq.status();
|
let stats = txq.status();
|
||||||
@ -2395,12 +2504,11 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_recalculate_height_when_removing_from_future() {
|
fn should_recalculate_height_when_removing_from_future() {
|
||||||
// given
|
// given
|
||||||
let previous_nonce = |a: &Address|
|
let previous_nonce = default_account_details().nonce - U256::one();
|
||||||
AccountDetails { nonce: default_account_details(a).nonce - U256::one(), balance: !U256::zero() };
|
|
||||||
let mut txq = TransactionQueue::default();
|
let mut txq = TransactionQueue::default();
|
||||||
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &previous_nonce, &gas_estimator).unwrap();
|
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(previous_nonce)).unwrap();
|
||||||
txq.add(tx2, TransactionOrigin::External, 0, None, &previous_nonce, &gas_estimator).unwrap();
|
txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(previous_nonce)).unwrap();
|
||||||
assert_eq!(txq.status().future, 2);
|
assert_eq!(txq.status().future, 2);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -2428,10 +2536,9 @@ mod test {
|
|||||||
let tx = new_tx_default();
|
let tx = new_tx_default();
|
||||||
let from = tx.sender();
|
let from = tx.sender();
|
||||||
let nonce = tx.nonce;
|
let nonce = tx.nonce;
|
||||||
let details = |_a: &Address| AccountDetails { nonce: nonce, balance: !U256::zero() };
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.add(tx, TransactionOrigin::External, 0, None, &details, &gas_estimator).unwrap();
|
txq.add(tx, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(nonce)).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(txq.last_nonce(&from), Some(nonce));
|
assert_eq!(txq.last_nonce(&from), Some(nonce));
|
||||||
@ -2443,10 +2550,9 @@ mod test {
|
|||||||
let mut txq = TransactionQueue::default();
|
let mut txq = TransactionQueue::default();
|
||||||
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
let (nonce1, nonce2) = (tx1.nonce, tx2.nonce);
|
let (nonce1, nonce2) = (tx1.nonce, tx2.nonce);
|
||||||
let details1 = |_a: &Address| AccountDetails { nonce: nonce1, balance: !U256::zero() };
|
|
||||||
|
|
||||||
// Insert first transaction
|
// Insert first transaction
|
||||||
txq.add(tx1, TransactionOrigin::External, 0, None, &details1, &gas_estimator).unwrap();
|
txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(nonce1)).unwrap();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.cull(tx2.sender(), nonce2 + U256::one());
|
txq.cull(tx2.sender(), nonce2 + U256::one());
|
||||||
@ -2462,13 +2568,12 @@ mod test {
|
|||||||
let (tx1, tx2) = new_tx_pair_default(4.into(), 0.into());
|
let (tx1, tx2) = new_tx_pair_default(4.into(), 0.into());
|
||||||
let sender = tx1.sender();
|
let sender = tx1.sender();
|
||||||
let (nonce1, nonce2) = (tx1.nonce, tx2.nonce);
|
let (nonce1, nonce2) = (tx1.nonce, tx2.nonce);
|
||||||
let details1 = |_a: &Address| AccountDetails { nonce: nonce1, balance: !U256::zero() };
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
// Insert first transaction
|
// Insert first transaction
|
||||||
assert_eq!(txq.add(tx1, TransactionOrigin::External, 0, None, &details1, &gas_estimator).unwrap(), TransactionImportResult::Current);
|
assert_eq!(txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(nonce1)).unwrap(), TransactionImportResult::Current);
|
||||||
// Second should go to future
|
// Second should go to future
|
||||||
assert_eq!(txq.add(tx2, TransactionOrigin::External, 0, None, &details1, &gas_estimator).unwrap(), TransactionImportResult::Future);
|
assert_eq!(txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(nonce1)).unwrap(), TransactionImportResult::Future);
|
||||||
// Now block is imported
|
// Now block is imported
|
||||||
txq.cull(sender, nonce2 - U256::from(1));
|
txq.cull(sender, nonce2 - U256::from(1));
|
||||||
// tx2 should be not be promoted to current
|
// tx2 should be not be promoted to current
|
||||||
@ -2487,9 +2592,9 @@ mod test {
|
|||||||
assert_eq!(txq.has_local_pending_transactions(), false);
|
assert_eq!(txq.has_local_pending_transactions(), false);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
assert_eq!(txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap(), TransactionImportResult::Current);
|
assert_eq!(txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(), TransactionImportResult::Current);
|
||||||
assert_eq!(txq.has_local_pending_transactions(), false);
|
assert_eq!(txq.has_local_pending_transactions(), false);
|
||||||
assert_eq!(txq.add(tx2, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap(), TransactionImportResult::Current);
|
assert_eq!(txq.add(tx2, TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap(), TransactionImportResult::Current);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(txq.has_local_pending_transactions(), true);
|
assert_eq!(txq.has_local_pending_transactions(), true);
|
||||||
@ -2500,12 +2605,11 @@ mod test {
|
|||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 1, !U256::zero(), !U256::zero());
|
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 1, !U256::zero(), !U256::zero());
|
||||||
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
let prev_nonce = |a: &Address| AccountDetails { nonce: default_account_details(a).nonce - U256::one(), balance:
|
let prev_nonce = default_account_details().nonce - U256::one();
|
||||||
default_account_details(a).balance };
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
assert_eq!(txq.add(tx2, TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap(), TransactionImportResult::Future);
|
assert_eq!(txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap(), TransactionImportResult::Future);
|
||||||
assert_eq!(txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &prev_nonce, &gas_estimator).unwrap(), TransactionImportResult::Future);
|
assert_eq!(txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider().with_account_nonce(prev_nonce)).unwrap(), TransactionImportResult::Future);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(txq.future.by_priority.len(), 1);
|
assert_eq!(txq.future.by_priority.len(), 1);
|
||||||
@ -2530,14 +2634,14 @@ mod test {
|
|||||||
(tx.sign(secret, None), tx2.sign(secret, None), tx2_2.sign(secret, None), tx3.sign(secret, None))
|
(tx.sign(secret, None), tx2.sign(secret, None), tx2_2.sign(secret, None), tx3.sign(secret, None))
|
||||||
};
|
};
|
||||||
let sender = tx1.sender();
|
let sender = tx1.sender();
|
||||||
txq.add(tx1, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx1, TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx2, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx2, TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx3, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx3, TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap();
|
||||||
assert_eq!(txq.future.by_priority.len(), 0);
|
assert_eq!(txq.future.by_priority.len(), 0);
|
||||||
assert_eq!(txq.current.by_priority.len(), 3);
|
assert_eq!(txq.current.by_priority.len(), 3);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let res = txq.add(tx2_2, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator);
|
let res = txq.add(tx2_2, TransactionOrigin::Local, 0, None, &default_tx_provider());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(txq.last_nonce(&sender).unwrap(), 125.into());
|
assert_eq!(txq.last_nonce(&sender).unwrap(), 125.into());
|
||||||
@ -2550,11 +2654,11 @@ mod test {
|
|||||||
// given
|
// given
|
||||||
let mut txq = TransactionQueue::default();
|
let mut txq = TransactionQueue::default();
|
||||||
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
|
||||||
let high_gas = |_: &SignedTransaction| 100_001.into();
|
let high_gas = 100_001.into();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let res1 = txq.add(tx1, TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator);
|
let res1 = txq.add(tx1, TransactionOrigin::Local, 0, None, &default_tx_provider());
|
||||||
let res2 = txq.add(tx2, TransactionOrigin::Local, 0, None, &default_account_details, &high_gas);
|
let res2 = txq.add(tx2, TransactionOrigin::Local, 0, None, &default_tx_provider().with_tx_gas_required(high_gas));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(res1.unwrap(), TransactionImportResult::Current);
|
assert_eq!(res1.unwrap(), TransactionImportResult::Current);
|
||||||
@ -2575,10 +2679,10 @@ mod test {
|
|||||||
AccountDetails { nonce: default_nonce() + U256::one(), balance: !U256::zero() };
|
AccountDetails { nonce: default_nonce() + U256::one(), balance: !U256::zero() };
|
||||||
|
|
||||||
// Insert all transactions
|
// Insert all transactions
|
||||||
txq.add(tx1, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx2, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx2, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx3, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx3, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx4, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx4, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
assert_eq!(txq.top_transactions().len(), 4);
|
assert_eq!(txq.top_transactions().len(), 4);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -2596,15 +2700,15 @@ mod test {
|
|||||||
let (tx3, tx4) = new_tx_pair_default(2.into(), 0.into());
|
let (tx3, tx4) = new_tx_pair_default(2.into(), 0.into());
|
||||||
|
|
||||||
// Insert all transactions
|
// Insert all transactions
|
||||||
txq.add(tx1.clone(), TransactionOrigin::Local, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx1.clone(), TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx2, TransactionOrigin::External, 5, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx2, TransactionOrigin::External, 5, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx3.clone(), TransactionOrigin::External, 10, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx3.clone(), TransactionOrigin::External, 10, None, &default_tx_provider()).unwrap();
|
||||||
txq.add(tx4, TransactionOrigin::External, 0, None, &default_account_details, &gas_estimator).unwrap();
|
txq.add(tx4, TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
|
||||||
assert_eq!(txq.top_transactions().len(), 3);
|
assert_eq!(txq.top_transactions().len(), 3);
|
||||||
assert_eq!(txq.future_transactions().len(), 1);
|
assert_eq!(txq.future_transactions().len(), 1);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
txq.remove_old(&default_account_details, 9 + super::DEFAULT_QUEUING_PERIOD);
|
txq.remove_old(&default_account_details_for_addr, 9 + super::DEFAULT_QUEUING_PERIOD);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(txq.top_transactions().len(), 2);
|
assert_eq!(txq.top_transactions().len(), 2);
|
||||||
@ -2612,4 +2716,75 @@ mod test {
|
|||||||
assert_eq!(txq.top_transactions(), vec![tx1, tx3]);
|
assert_eq!(txq.top_transactions(), vec![tx1, tx3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_accept_local_service_transaction() {
|
||||||
|
// given
|
||||||
|
let tx = new_tx(123.into(), 0.into());
|
||||||
|
let mut txq = TransactionQueue::default();
|
||||||
|
txq.set_minimal_gas_price(100.into());
|
||||||
|
|
||||||
|
// when
|
||||||
|
txq.add(tx, TransactionOrigin::Local, 0, None, &default_tx_provider()).unwrap();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(txq.top_transactions().len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_not_accept_external_service_transaction_if_sender_not_certified() {
|
||||||
|
// given
|
||||||
|
let tx1 = new_tx(123.into(), 0.into());
|
||||||
|
let tx2 = new_tx(456.into(), 0.into());
|
||||||
|
let mut txq = TransactionQueue::default();
|
||||||
|
txq.set_minimal_gas_price(100.into());
|
||||||
|
|
||||||
|
// when
|
||||||
|
assert_eq!(unwrap_tx_err(txq.add(tx1, TransactionOrigin::External, 0, None, &default_tx_provider())),
|
||||||
|
TransactionError::InsufficientGasPrice {
|
||||||
|
minimal: 100.into(),
|
||||||
|
got: 0.into(),
|
||||||
|
});
|
||||||
|
assert_eq!(unwrap_tx_err(txq.add(tx2, TransactionOrigin::RetractedBlock, 0, None, &default_tx_provider())),
|
||||||
|
TransactionError::InsufficientGasPrice {
|
||||||
|
minimal: 100.into(),
|
||||||
|
got: 0.into(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(txq.top_transactions().len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_not_accept_external_service_transaction_if_contract_returns_error() {
|
||||||
|
// given
|
||||||
|
let tx = new_tx(123.into(), 0.into());
|
||||||
|
let mut txq = TransactionQueue::default();
|
||||||
|
txq.set_minimal_gas_price(100.into());
|
||||||
|
|
||||||
|
// when
|
||||||
|
let details_provider = default_tx_provider().service_transaction_checker_returns_error("Contract error");
|
||||||
|
assert_eq!(unwrap_tx_err(txq.add(tx, TransactionOrigin::External, 0, None, &details_provider)),
|
||||||
|
TransactionError::InsufficientGasPrice {
|
||||||
|
minimal: 100.into(),
|
||||||
|
got: 0.into(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(txq.top_transactions().len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_accept_external_service_transaction_if_sender_is_certified() {
|
||||||
|
// given
|
||||||
|
let tx = new_tx(123.into(), 0.into());
|
||||||
|
let mut txq = TransactionQueue::default();
|
||||||
|
txq.set_minimal_gas_price(100.into());
|
||||||
|
|
||||||
|
// when
|
||||||
|
let details_provider = default_tx_provider().service_transaction_checker_accepts(true);
|
||||||
|
txq.add(tx, TransactionOrigin::External, 0, None, &details_provider).unwrap();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(txq.top_transactions().len(), 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,6 +90,7 @@ tx_time_limit = 100 #ms
|
|||||||
extra_data = "Parity"
|
extra_data = "Parity"
|
||||||
remove_solved = false
|
remove_solved = false
|
||||||
notify_work = ["http://localhost:3001"]
|
notify_work = ["http://localhost:3001"]
|
||||||
|
refuse_service_transactions = false
|
||||||
|
|
||||||
[footprint]
|
[footprint]
|
||||||
tracing = "auto"
|
tracing = "auto"
|
||||||
|
@ -230,6 +230,8 @@ usage! {
|
|||||||
or |c: &Config| otry!(c.mining).remove_solved.clone(),
|
or |c: &Config| otry!(c.mining).remove_solved.clone(),
|
||||||
flag_notify_work: Option<String> = None,
|
flag_notify_work: Option<String> = None,
|
||||||
or |c: &Config| otry!(c.mining).notify_work.clone().map(|vec| Some(vec.join(","))),
|
or |c: &Config| otry!(c.mining).notify_work.clone().map(|vec| Some(vec.join(","))),
|
||||||
|
flag_refuse_service_transactions: bool = false,
|
||||||
|
or |c: &Config| otry!(c.mining).refuse_service_transactions.clone(),
|
||||||
|
|
||||||
// -- Footprint Options
|
// -- Footprint Options
|
||||||
flag_tracing: String = "auto",
|
flag_tracing: String = "auto",
|
||||||
@ -416,6 +418,7 @@ struct Mining {
|
|||||||
tx_queue_ban_time: Option<u16>,
|
tx_queue_ban_time: Option<u16>,
|
||||||
remove_solved: Option<bool>,
|
remove_solved: Option<bool>,
|
||||||
notify_work: Option<Vec<String>>,
|
notify_work: Option<Vec<String>>,
|
||||||
|
refuse_service_transactions: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, PartialEq, RustcDecodable)]
|
#[derive(Default, Debug, PartialEq, RustcDecodable)]
|
||||||
@ -633,6 +636,7 @@ mod tests {
|
|||||||
flag_tx_queue_ban_time: 180u16,
|
flag_tx_queue_ban_time: 180u16,
|
||||||
flag_remove_solved: false,
|
flag_remove_solved: false,
|
||||||
flag_notify_work: Some("http://localhost:3001".into()),
|
flag_notify_work: Some("http://localhost:3001".into()),
|
||||||
|
flag_refuse_service_transactions: false,
|
||||||
|
|
||||||
// -- Footprint Options
|
// -- Footprint Options
|
||||||
flag_tracing: "auto".into(),
|
flag_tracing: "auto".into(),
|
||||||
@ -811,6 +815,7 @@ mod tests {
|
|||||||
extra_data: None,
|
extra_data: None,
|
||||||
remove_solved: None,
|
remove_solved: None,
|
||||||
notify_work: None,
|
notify_work: None,
|
||||||
|
refuse_service_transactions: None,
|
||||||
}),
|
}),
|
||||||
footprint: Some(Footprint {
|
footprint: Some(Footprint {
|
||||||
tracing: Some("on".into()),
|
tracing: Some("on".into()),
|
||||||
|
@ -258,6 +258,8 @@ Sealing/Mining Options:
|
|||||||
--notify-work URLS URLs to which work package notifications are pushed.
|
--notify-work URLS URLs to which work package notifications are pushed.
|
||||||
URLS should be a comma-delimited list of HTTP URLs.
|
URLS should be a comma-delimited list of HTTP URLs.
|
||||||
(default: {flag_notify_work:?})
|
(default: {flag_notify_work:?})
|
||||||
|
--refuse-service-transactions Always refuse service transactions.
|
||||||
|
(default: {flag_refuse_service_transactions}).
|
||||||
|
|
||||||
Footprint Options:
|
Footprint Options:
|
||||||
--tracing BOOL Indicates if full transaction tracing should be
|
--tracing BOOL Indicates if full transaction tracing should be
|
||||||
|
@ -491,7 +491,8 @@ impl Configuration {
|
|||||||
ban_duration: Duration::from_secs(self.args.flag_tx_queue_ban_time as u64),
|
ban_duration: Duration::from_secs(self.args.flag_tx_queue_ban_time as u64),
|
||||||
},
|
},
|
||||||
None => Banning::Disabled,
|
None => Banning::Disabled,
|
||||||
}
|
},
|
||||||
|
refuse_service_transactions: self.args.flag_refuse_service_transactions,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(options)
|
Ok(options)
|
||||||
|
@ -66,6 +66,7 @@ fn miner_service(spec: &Spec, accounts: Arc<AccountProvider>) -> Arc<Miner> {
|
|||||||
reseal_min_period: Duration::from_secs(0),
|
reseal_min_period: Duration::from_secs(0),
|
||||||
work_queue_size: 50,
|
work_queue_size: 50,
|
||||||
enable_resubmission: true,
|
enable_resubmission: true,
|
||||||
|
refuse_service_transactions: false,
|
||||||
},
|
},
|
||||||
GasPricer::new_fixed(20_000_000_000u64.into()),
|
GasPricer::new_fixed(20_000_000_000u64.into()),
|
||||||
&spec,
|
&spec,
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
// Rust/Parity ABI struct autogenerator.
|
// Rust/Parity ABI struct autogenerator.
|
||||||
// By Gav Wood, 2016.
|
// By Gav Wood, 2016.
|
||||||
|
|
||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
String.prototype.replaceAll = function(f, t) { return this.split(f).join(t); }
|
String.prototype.replaceAll = function(f, t) { return this.split(f).join(t); }
|
||||||
String.prototype.toSnake = function(){
|
String.prototype.toSnake = function(){
|
||||||
return this.replace(/([A-Z])/g, function($1){return "_"+$1.toLowerCase();});
|
return this.replace(/([A-Z])/g, function($1){return "_"+$1.toLowerCase();});
|
||||||
@ -24,6 +26,7 @@ String.prototype.toSnake = function(){
|
|||||||
|
|
||||||
function makeContractFile(name, json, prefs) {
|
function makeContractFile(name, json, prefs) {
|
||||||
return `// Autogenerated from JSON contract definition using Rust contract convertor.
|
return `// Autogenerated from JSON contract definition using Rust contract convertor.
|
||||||
|
// Command line: ${process.argv.slice(2).join(' ')}
|
||||||
#![allow(unused_imports)]
|
#![allow(unused_imports)]
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
@ -39,14 +42,15 @@ function convertContract(name, json, prefs) {
|
|||||||
return `${prefs._pub ? "pub " : ""}struct ${name} {
|
return `${prefs._pub ? "pub " : ""}struct ${name} {
|
||||||
contract: ethabi::Contract,
|
contract: ethabi::Contract,
|
||||||
address: util::Address,
|
address: util::Address,
|
||||||
do_call: Box<Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send ${prefs._sync ? "+ Sync " : ""}+ 'static>,
|
${prefs._explicit_do_call ? "" : `do_call: Box<Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send${prefs._sync ? " + Sync " : ""}+ 'static>,`}
|
||||||
}
|
}
|
||||||
impl ${name} {
|
impl ${name} {
|
||||||
pub fn new<F>(address: util::Address, do_call: F) -> Self where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send ${prefs._sync ? "+ Sync " : ""}+ 'static {
|
pub fn new${prefs._explicit_do_call ? "" : "<F>"}(address: util::Address${prefs._explicit_do_call ? "" : `", do_call: F"`}) -> Self
|
||||||
|
${prefs._explicit_do_call ? "" : `where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send ${prefs._sync ? "+ Sync " : ""}+ 'static`} {
|
||||||
${name} {
|
${name} {
|
||||||
contract: ethabi::Contract::new(ethabi::Interface::load(b"${JSON.stringify(json.filter(a => a.type == 'function')).replaceAll('"', '\\"')}").expect("JSON is autogenerated; qed")),
|
contract: ethabi::Contract::new(ethabi::Interface::load(b"${JSON.stringify(json.filter(a => a.type == 'function')).replaceAll('"', '\\"')}").expect("JSON is autogenerated; qed")),
|
||||||
address: address,
|
address: address,
|
||||||
do_call: Box::new(do_call),
|
${prefs._explicit_do_call ? "" : `do_call: Box::new(do_call),`}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn as_string<T: fmt::Debug>(e: T) -> String { format!("{:?}", e) }
|
fn as_string<T: fmt::Debug>(e: T) -> String { format!("{:?}", e) }
|
||||||
@ -205,6 +209,7 @@ function tokenExtract(expr, type, _prefs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function convertFunction(json, _prefs) {
|
function convertFunction(json, _prefs) {
|
||||||
|
let cprefs = _prefs || {};
|
||||||
let prefs = (_prefs || {})[json.name] || (_prefs || {})['_'] || {};
|
let prefs = (_prefs || {})[json.name] || (_prefs || {})['_'] || {};
|
||||||
let snakeName = json.name.toSnake();
|
let snakeName = json.name.toSnake();
|
||||||
let params = json.inputs.map((x, i) => (x.name ? x.name.toSnake() : ("_" + (i + 1))) + ": " + mapType(x.name, x.type, prefs[x.name]));
|
let params = json.inputs.map((x, i) => (x.name ? x.name.toSnake() : ("_" + (i + 1))) + ": " + mapType(x.name, x.type, prefs[x.name]));
|
||||||
@ -212,18 +217,35 @@ function convertFunction(json, _prefs) {
|
|||||||
return `
|
return `
|
||||||
/// Auto-generated from: \`${JSON.stringify(json)}\`
|
/// Auto-generated from: \`${JSON.stringify(json)}\`
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn ${snakeName}(&self${params.length > 0 ? ', ' + params.join(", ") : ''}) -> Result<${returns}, String> {
|
pub fn ${snakeName}${cprefs._explicit_do_call ? "<F>" : ""}(&self${cprefs._explicit_do_call ? `, do_call: &F` : ""}${params.length > 0 ? ', ' + params.join(", ") : ''}) -> Result<${returns}, String>
|
||||||
|
${cprefs._explicit_do_call ? `where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send ${prefs._sync ? "+ Sync " : ""}` : ""} {
|
||||||
let call = self.contract.function("${json.name}".into()).map_err(Self::as_string)?;
|
let call = self.contract.function("${json.name}".into()).map_err(Self::as_string)?;
|
||||||
let data = call.encode_call(
|
let data = call.encode_call(
|
||||||
vec![${json.inputs.map((x, i) => convertToken(x.name ? x.name.toSnake() : ("_" + (i + 1)), x.type, prefs[x.name])).join(', ')}]
|
vec![${json.inputs.map((x, i) => convertToken(x.name ? x.name.toSnake() : ("_" + (i + 1)), x.type, prefs[x.name])).join(', ')}]
|
||||||
).map_err(Self::as_string)?;
|
).map_err(Self::as_string)?;
|
||||||
${json.outputs.length > 0 ? 'let output = ' : ''}call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
${json.outputs.length > 0 ? 'let output = ' : ''}call.decode_output((${cprefs._explicit_do_call ? "" : "self."}do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||||
${json.outputs.length > 0 ? 'let mut result = output.into_iter().rev().collect::<Vec<_>>();' : ''}
|
${json.outputs.length > 0 ? 'let mut result = output.into_iter().rev().collect::<Vec<_>>();' : ''}
|
||||||
Ok((${json.outputs.map((o, i) => tokenExtract('result.pop().ok_or("Invalid return arity")?', o.type, prefs[o.name])).join(', ')}))
|
Ok((${json.outputs.map((o, i) => tokenExtract('result.pop().ok_or("Invalid return arity")?', o.type, prefs[o.name])).join(', ')}))
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// default preferences:
|
||||||
|
let prefs = {"_pub": true, "_": {"_client": {"string": true}, "_platform": {"string": true}}, "_sync": true};
|
||||||
|
// default contract json ABI
|
||||||
let jsonabi = [{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}];
|
let jsonabi = [{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}];
|
||||||
|
|
||||||
let out = makeContractFile("Contract", jsonabi, {"_pub": true, "_": {"_client": {"string": true}, "_platform": {"string": true}}, "_sync": true});
|
// parse command line options
|
||||||
|
for (let i = 1; i < process.argv.length; ++i) {
|
||||||
|
let arg = process.argv[i];
|
||||||
|
if (arg.indexOf("--jsonabi") == 0) {
|
||||||
|
jsonabi = arg.slice(10);
|
||||||
|
if (fs.existsSync(jsonabi)) {
|
||||||
|
jsonabi = JSON.parse(fs.readFileSync(jsonabi).toString());
|
||||||
|
}
|
||||||
|
} else if (arg.indexOf("--explicit-do-call") == 0) {
|
||||||
|
prefs._explicit_do_call = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let out = makeContractFile("Contract", jsonabi, prefs);
|
||||||
console.log(`${out}`);
|
console.log(`${out}`);
|
||||||
|
@ -96,6 +96,7 @@ use ethcore::header::{BlockNumber, Header as BlockHeader};
|
|||||||
use ethcore::client::{BlockChainClient, BlockStatus, BlockId, BlockChainInfo, BlockImportError, BlockQueueInfo};
|
use ethcore::client::{BlockChainClient, BlockStatus, BlockId, BlockChainInfo, BlockImportError, BlockQueueInfo};
|
||||||
use ethcore::error::*;
|
use ethcore::error::*;
|
||||||
use ethcore::snapshot::{ManifestData, RestorationStatus};
|
use ethcore::snapshot::{ManifestData, RestorationStatus};
|
||||||
|
use ethcore::transaction::PendingTransaction;
|
||||||
use sync_io::SyncIo;
|
use sync_io::SyncIo;
|
||||||
use time;
|
use time;
|
||||||
use super::SyncConfig;
|
use super::SyncConfig;
|
||||||
@ -1949,7 +1950,46 @@ impl ChainSync {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let all_transactions_hashes = transactions.iter().map(|tx| tx.transaction.hash()).collect::<HashSet<H256>>();
|
let (transactions, service_transactions): (Vec<_>, Vec<_>) = transactions.into_iter()
|
||||||
|
.partition(|tx| !tx.transaction.gas_price.is_zero());
|
||||||
|
|
||||||
|
// usual transactions could be propagated to all peers
|
||||||
|
let mut affected_peers = HashSet::new();
|
||||||
|
if !transactions.is_empty() {
|
||||||
|
let peers = self.select_peers_for_transactions(|_| true);
|
||||||
|
affected_peers = self.propagate_transactions_to_peers(io, peers, transactions);
|
||||||
|
}
|
||||||
|
|
||||||
|
// most of times service_transactions will be empty
|
||||||
|
// => there's no need to merge packets
|
||||||
|
if !service_transactions.is_empty() {
|
||||||
|
let service_transactions_peers = self.select_peers_for_transactions(|peer_id| accepts_service_transaction(&io.peer_info(*peer_id)));
|
||||||
|
let service_transactions_affected_peers = self.propagate_transactions_to_peers(io, service_transactions_peers, service_transactions);
|
||||||
|
affected_peers.extend(&service_transactions_affected_peers);
|
||||||
|
}
|
||||||
|
|
||||||
|
affected_peers.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn select_peers_for_transactions<F>(&self, filter: F) -> Vec<PeerId>
|
||||||
|
where F: Fn(&PeerId) -> bool {
|
||||||
|
// sqrt(x)/x scaled to max u32
|
||||||
|
let fraction = (self.peers.len() as f64).powf(-0.5).mul(u32::max_value() as f64).round() as u32;
|
||||||
|
let small = self.peers.len() < MIN_PEERS_PROPAGATION;
|
||||||
|
|
||||||
|
let mut random = random::new();
|
||||||
|
self.peers.keys()
|
||||||
|
.cloned()
|
||||||
|
.filter(filter)
|
||||||
|
.filter(|_| small || random.next_u32() < fraction)
|
||||||
|
.take(MAX_PEERS_PROPAGATION)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn propagate_transactions_to_peers(&mut self, io: &mut SyncIo, peers: Vec<PeerId>, transactions: Vec<PendingTransaction>) -> HashSet<PeerId> {
|
||||||
|
let all_transactions_hashes = transactions.iter()
|
||||||
|
.map(|tx| tx.transaction.hash())
|
||||||
|
.collect::<HashSet<H256>>();
|
||||||
let all_transactions_rlp = {
|
let all_transactions_rlp = {
|
||||||
let mut packet = RlpStream::new_list(transactions.len());
|
let mut packet = RlpStream::new_list(transactions.len());
|
||||||
for tx in &transactions { packet.append(&tx.transaction); }
|
for tx in &transactions { packet.append(&tx.transaction); }
|
||||||
@ -1960,26 +2000,24 @@ impl ChainSync {
|
|||||||
self.transactions_stats.retain(&all_transactions_hashes);
|
self.transactions_stats.retain(&all_transactions_hashes);
|
||||||
|
|
||||||
// sqrt(x)/x scaled to max u32
|
// sqrt(x)/x scaled to max u32
|
||||||
let fraction = (self.peers.len() as f64).powf(-0.5).mul(u32::max_value() as f64).round() as u32;
|
|
||||||
let small = self.peers.len() < MIN_PEERS_PROPAGATION;
|
|
||||||
let block_number = io.chain().chain_info().best_block_number;
|
let block_number = io.chain().chain_info().best_block_number;
|
||||||
|
|
||||||
let mut random = random::new();
|
|
||||||
let lucky_peers = {
|
let lucky_peers = {
|
||||||
|
peers.into_iter()
|
||||||
|
.filter_map(|peer_id| {
|
||||||
let stats = &mut self.transactions_stats;
|
let stats = &mut self.transactions_stats;
|
||||||
self.peers.iter_mut()
|
let peer_info = self.peers.get_mut(&peer_id)
|
||||||
.filter(|_| small || random.next_u32() < fraction)
|
.expect("peer_id is form peers; peers is result of select_peers_for_transactions; select_peers_for_transactions selects peers from self.peers; qed");
|
||||||
.take(MAX_PEERS_PROPAGATION)
|
|
||||||
.filter_map(|(peer_id, mut peer_info)| {
|
|
||||||
// Send all transactions
|
// Send all transactions
|
||||||
if peer_info.last_sent_transactions.is_empty() {
|
if peer_info.last_sent_transactions.is_empty() {
|
||||||
// update stats
|
// update stats
|
||||||
for hash in &all_transactions_hashes {
|
for hash in &all_transactions_hashes {
|
||||||
let id = io.peer_session_info(*peer_id).and_then(|info| info.id);
|
let id = io.peer_session_info(peer_id).and_then(|info| info.id);
|
||||||
stats.propagated(*hash, id, block_number);
|
stats.propagated(*hash, id, block_number);
|
||||||
}
|
}
|
||||||
peer_info.last_sent_transactions = all_transactions_hashes.clone();
|
peer_info.last_sent_transactions = all_transactions_hashes.clone();
|
||||||
return Some((*peer_id, all_transactions_hashes.len(), all_transactions_rlp.clone()));
|
return Some((peer_id, all_transactions_hashes.len(), all_transactions_rlp.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get hashes of all transactions to send to this peer
|
// Get hashes of all transactions to send to this peer
|
||||||
@ -1997,7 +2035,7 @@ impl ChainSync {
|
|||||||
if to_send.contains(&tx.transaction.hash()) {
|
if to_send.contains(&tx.transaction.hash()) {
|
||||||
packet.append(&tx.transaction);
|
packet.append(&tx.transaction);
|
||||||
// update stats
|
// update stats
|
||||||
let id = io.peer_session_info(*peer_id).and_then(|info| info.id);
|
let id = io.peer_session_info(peer_id).and_then(|info| info.id);
|
||||||
stats.propagated(tx.transaction.hash(), id, block_number);
|
stats.propagated(tx.transaction.hash(), id, block_number);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2007,22 +2045,25 @@ impl ChainSync {
|
|||||||
.chain(&to_send)
|
.chain(&to_send)
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect();
|
.collect();
|
||||||
Some((*peer_id, to_send.len(), packet.out()))
|
Some((peer_id, to_send.len(), packet.out()))
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Send RLPs
|
// Send RLPs
|
||||||
let peers = lucky_peers.len();
|
let mut peers = HashSet::new();
|
||||||
if peers > 0 {
|
if lucky_peers.len() > 0 {
|
||||||
let mut max_sent = 0;
|
let mut max_sent = 0;
|
||||||
|
let lucky_peers_len = lucky_peers.len();
|
||||||
for (peer_id, sent, rlp) in lucky_peers {
|
for (peer_id, sent, rlp) in lucky_peers {
|
||||||
|
peers.insert(peer_id);
|
||||||
self.send_packet(io, peer_id, TRANSACTIONS_PACKET, rlp);
|
self.send_packet(io, peer_id, TRANSACTIONS_PACKET, rlp);
|
||||||
trace!(target: "sync", "{:02} <- Transactions ({} entries)", peer_id, sent);
|
trace!(target: "sync", "{:02} <- Transactions ({} entries)", peer_id, sent);
|
||||||
max_sent = max(max_sent, sent);
|
max_sent = max(max_sent, sent);
|
||||||
}
|
}
|
||||||
debug!(target: "sync", "Sent up to {} transactions to {} peers.", max_sent, peers);
|
debug!(target: "sync", "Sent up to {} transactions to {} peers.", max_sent, lucky_peers_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
peers
|
peers
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2109,12 +2150,30 @@ impl ChainSync {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if peer is able to process service transactions
|
||||||
|
fn accepts_service_transaction(client_id: &str) -> bool {
|
||||||
|
// Parity versions starting from this will accept service-transactions
|
||||||
|
const SERVICE_TRANSACTIONS_VERSION: (u32, u32) = (1u32, 6u32);
|
||||||
|
// Parity client string prefix
|
||||||
|
const PARITY_CLIENT_ID_PREFIX: &'static str = "Parity/v";
|
||||||
|
|
||||||
|
if !client_id.starts_with(PARITY_CLIENT_ID_PREFIX) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let ver: Vec<u32> = client_id[PARITY_CLIENT_ID_PREFIX.len()..].split('.')
|
||||||
|
.take(2)
|
||||||
|
.filter_map(|s| s.parse().ok())
|
||||||
|
.collect();
|
||||||
|
ver.len() == 2 && (ver[0] > SERVICE_TRANSACTIONS_VERSION.0 || (ver[0] == SERVICE_TRANSACTIONS_VERSION.0 && ver[1] >= SERVICE_TRANSACTIONS_VERSION.1))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::collections::{HashSet, VecDeque};
|
use std::collections::{HashSet, VecDeque};
|
||||||
|
use network::PeerId;
|
||||||
use tests::helpers::*;
|
use tests::helpers::*;
|
||||||
use tests::snapshot::TestSnapshotService;
|
use tests::snapshot::TestSnapshotService;
|
||||||
use util::{U256, Address, RwLock};
|
use util::{Uint, U256, Address, RwLock};
|
||||||
use util::sha3::Hashable;
|
use util::sha3::Hashable;
|
||||||
use util::hash::{H256, FixedHash};
|
use util::hash::{H256, FixedHash};
|
||||||
use util::bytes::Bytes;
|
use util::bytes::Bytes;
|
||||||
@ -2351,7 +2410,12 @@ mod tests {
|
|||||||
|
|
||||||
fn dummy_sync_with_peer(peer_latest_hash: H256, client: &BlockChainClient) -> ChainSync {
|
fn dummy_sync_with_peer(peer_latest_hash: H256, client: &BlockChainClient) -> ChainSync {
|
||||||
let mut sync = ChainSync::new(SyncConfig::default(), client);
|
let mut sync = ChainSync::new(SyncConfig::default(), client);
|
||||||
sync.peers.insert(0,
|
insert_dummy_peer(&mut sync, 0, peer_latest_hash);
|
||||||
|
sync
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_dummy_peer(sync: &mut ChainSync, peer_id: PeerId, peer_latest_hash: H256) {
|
||||||
|
sync.peers.insert(peer_id,
|
||||||
PeerInfo {
|
PeerInfo {
|
||||||
protocol_version: 0,
|
protocol_version: 0,
|
||||||
genesis: H256::zero(),
|
genesis: H256::zero(),
|
||||||
@ -2370,7 +2434,7 @@ mod tests {
|
|||||||
asking_snapshot_data: None,
|
asking_snapshot_data: None,
|
||||||
block_set: None,
|
block_set: None,
|
||||||
});
|
});
|
||||||
sync
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -2622,6 +2686,79 @@ mod tests {
|
|||||||
assert_eq!(stats.len(), 1, "Should maintain stats for single transaction.")
|
assert_eq!(stats.len(), 1, "Should maintain stats for single transaction.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_propagate_service_transaction_to_selected_peers_only() {
|
||||||
|
let mut client = TestBlockChainClient::new();
|
||||||
|
client.insert_transaction_with_gas_price_to_queue(U256::zero());
|
||||||
|
let block_hash = client.block_hash_delta_minus(1);
|
||||||
|
let mut sync = ChainSync::new(SyncConfig::default(), &client);
|
||||||
|
let queue = RwLock::new(VecDeque::new());
|
||||||
|
let ss = TestSnapshotService::new();
|
||||||
|
let mut io = TestIo::new(&mut client, &ss, &queue, None);
|
||||||
|
|
||||||
|
// when peer#1 is Geth
|
||||||
|
insert_dummy_peer(&mut sync, 1, block_hash);
|
||||||
|
io.peers_info.insert(1, "Geth".to_owned());
|
||||||
|
// and peer#2 is Parity, accepting service transactions
|
||||||
|
insert_dummy_peer(&mut sync, 2, block_hash);
|
||||||
|
io.peers_info.insert(2, "Parity/v1.6".to_owned());
|
||||||
|
// and peer#3 is Parity, discarding service transactions
|
||||||
|
insert_dummy_peer(&mut sync, 3, block_hash);
|
||||||
|
io.peers_info.insert(3, "Parity/v1.5".to_owned());
|
||||||
|
// and peer#4 is Parity, accepting service transactions
|
||||||
|
insert_dummy_peer(&mut sync, 4, block_hash);
|
||||||
|
io.peers_info.insert(4, "Parity/v1.7.3-ABCDEFGH".to_owned());
|
||||||
|
|
||||||
|
// and new service transaction is propagated to peers
|
||||||
|
sync.propagate_new_transactions(&mut io);
|
||||||
|
|
||||||
|
// peer#2 && peer#4 are receiving service transaction
|
||||||
|
assert!(io.packets.iter().any(|p| p.packet_id == 0x02 && p.recipient == 2)); // TRANSACTIONS_PACKET
|
||||||
|
assert!(io.packets.iter().any(|p| p.packet_id == 0x02 && p.recipient == 4)); // TRANSACTIONS_PACKET
|
||||||
|
assert_eq!(io.packets.len(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_propagate_service_transaction_is_sent_as_separate_message() {
|
||||||
|
let mut client = TestBlockChainClient::new();
|
||||||
|
let tx1_hash = client.insert_transaction_to_queue();
|
||||||
|
let tx2_hash = client.insert_transaction_with_gas_price_to_queue(U256::zero());
|
||||||
|
let block_hash = client.block_hash_delta_minus(1);
|
||||||
|
let mut sync = ChainSync::new(SyncConfig::default(), &client);
|
||||||
|
let queue = RwLock::new(VecDeque::new());
|
||||||
|
let ss = TestSnapshotService::new();
|
||||||
|
let mut io = TestIo::new(&mut client, &ss, &queue, None);
|
||||||
|
|
||||||
|
// when peer#1 is Parity, accepting service transactions
|
||||||
|
insert_dummy_peer(&mut sync, 1, block_hash);
|
||||||
|
io.peers_info.insert(1, "Parity/v1.6".to_owned());
|
||||||
|
|
||||||
|
// and service + non-service transactions are propagated to peers
|
||||||
|
sync.propagate_new_transactions(&mut io);
|
||||||
|
|
||||||
|
// two separate packets for peer are queued:
|
||||||
|
// 1) with non-service-transaction
|
||||||
|
// 2) with service transaction
|
||||||
|
let sent_transactions: Vec<UnverifiedTransaction> = io.packets.iter()
|
||||||
|
.filter_map(|p| {
|
||||||
|
if p.packet_id != 0x02 || p.recipient != 1 { // TRANSACTIONS_PACKET
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rlp = UntrustedRlp::new(&*p.data);
|
||||||
|
let item_count = rlp.item_count();
|
||||||
|
if item_count != 1 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
rlp.at(0).ok().and_then(|r| r.as_val().ok())
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
assert_eq!(sent_transactions.len(), 2);
|
||||||
|
assert!(sent_transactions.iter().any(|tx| tx.hash() == tx1_hash));
|
||||||
|
assert!(sent_transactions.iter().any(|tx| tx.hash() == tx2_hash));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn handles_peer_new_block_malformed() {
|
fn handles_peer_new_block_malformed() {
|
||||||
let mut client = TestBlockChainClient::new();
|
let mut client = TestBlockChainClient::new();
|
||||||
|
@ -50,6 +50,7 @@ pub struct TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p {
|
|||||||
pub sender: Option<PeerId>,
|
pub sender: Option<PeerId>,
|
||||||
pub to_disconnect: HashSet<PeerId>,
|
pub to_disconnect: HashSet<PeerId>,
|
||||||
pub packets: Vec<TestPacket>,
|
pub packets: Vec<TestPacket>,
|
||||||
|
pub peers_info: HashMap<PeerId, String>,
|
||||||
overlay: RwLock<HashMap<BlockNumber, Bytes>>,
|
overlay: RwLock<HashMap<BlockNumber, Bytes>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,6 +64,7 @@ impl<'p, C> TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p {
|
|||||||
to_disconnect: HashSet::new(),
|
to_disconnect: HashSet::new(),
|
||||||
overlay: RwLock::new(HashMap::new()),
|
overlay: RwLock::new(HashMap::new()),
|
||||||
packets: Vec::new(),
|
packets: Vec::new(),
|
||||||
|
peers_info: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,6 +114,12 @@ impl<'p, C> SyncIo for TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p {
|
|||||||
&*self.chain
|
&*self.chain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn peer_info(&self, peer_id: PeerId) -> String {
|
||||||
|
self.peers_info.get(&peer_id)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_else(|| peer_id.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
fn snapshot_service(&self) -> &SnapshotService {
|
fn snapshot_service(&self) -> &SnapshotService {
|
||||||
self.snapshot_service
|
self.snapshot_service
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user