Allow disabling local-by-default for transactions with new config entry (#8882)

* Add tx_queue_allow_unknown_local config option

- Previous commit messages:

dispatcher checks if we have the sender account

Add `tx_queue_allow_unknown_local` to MinerOptions

Add `tx_queue_allow_unknown_local` to config

fix order in MinerOptions to match Configuration

add cli flag for tx_queue_allow_unknown_local

Update refs to `tx_queue_allow_unknown_local`

Add tx_queue_allow_unknown_local to config test

revert changes to dispatcher

Move tx_queue_allow_unknown_local to `import_own_transaction`

Fix var name

if statement should return the values

derp de derp derp derp semicolons

Reset dispatch file to how it was before

fix compile issues + change from FLAG to ARG

add test and use `into`

import MinerOptions, clone the secret

Fix tests?

Compiler/linter issues fixed

Fix linter msg - case of constants

IT LIVES

refactor to omit yucky explict return

update comments

Fix based on diff AccountProvider.has_account method

* Refactor flag name + don't change import_own_tx behaviour

fix arg name

Note: force commit to try and get gitlab tests working again 😠

* Add fn to TestMinerService

* Avoid race condition from trusted sources

- refactor the miner tests a bit to cut down on code reuse
- add `trusted` param to dispatch_transaction and import_claimed_local_transaction

Add param to `import_claimed_local_transaction`

Fix fn sig in tests
This commit is contained in:
Max Kaye 2018-06-18 23:32:18 +10:00 committed by Tomasz Drwięga
parent 609d83f92c
commit 6004c394d6
8 changed files with 108 additions and 6 deletions

View File

@ -125,6 +125,8 @@ pub struct MinerOptions {
pub tx_queue_strategy: PrioritizationStrategy, pub tx_queue_strategy: PrioritizationStrategy,
/// Simple senders penalization. /// Simple senders penalization.
pub tx_queue_penalization: Penalization, pub tx_queue_penalization: Penalization,
/// Do we want to mark transactions recieved locally (e.g. RPC) as local if we don't have the sending account?
pub tx_queue_no_unfamiliar_locals: bool,
/// Do we refuse to accept service transactions even if sender is certified. /// Do we refuse to accept service transactions even if sender is certified.
pub refuse_service_transactions: bool, pub refuse_service_transactions: bool,
/// Transaction pool limits. /// Transaction pool limits.
@ -148,6 +150,7 @@ impl Default for MinerOptions {
infinite_pending_block: false, infinite_pending_block: false,
tx_queue_strategy: PrioritizationStrategy::GasPriceOnly, tx_queue_strategy: PrioritizationStrategy::GasPriceOnly,
tx_queue_penalization: Penalization::Disabled, tx_queue_penalization: Penalization::Disabled,
tx_queue_no_unfamiliar_locals: false,
refuse_service_transactions: false, refuse_service_transactions: false,
pool_limits: pool::Options { pool_limits: pool::Options {
max_count: 8_192, max_count: 8_192,
@ -794,8 +797,9 @@ impl miner::MinerService for Miner {
fn import_own_transaction<C: miner::BlockChainClient>( fn import_own_transaction<C: miner::BlockChainClient>(
&self, &self,
chain: &C, chain: &C,
pending: PendingTransaction, pending: PendingTransaction
) -> Result<(), transaction::Error> { ) -> Result<(), transaction::Error> {
// note: you may want to use `import_claimed_local_transaction` instead of this one.
trace!(target: "own_tx", "Importing transaction: {:?}", pending); trace!(target: "own_tx", "Importing transaction: {:?}", pending);
@ -816,6 +820,28 @@ impl miner::MinerService for Miner {
imported imported
} }
fn import_claimed_local_transaction<C: miner::BlockChainClient>(
&self,
chain: &C,
pending: PendingTransaction,
trusted: bool
) -> Result<(), transaction::Error> {
// treat the tx as local if the option is enabled, or if we have the account
let sender = pending.sender();
let treat_as_local = trusted
|| !self.options.tx_queue_no_unfamiliar_locals
|| self.accounts.as_ref().map(|accts| accts.has_account(sender)).unwrap_or(false);
if treat_as_local {
self.import_own_transaction(chain, pending)
} else {
// We want to replicate behaviour for external transactions if we're not going to treat
// this as local. This is important with regards to sealing blocks
self.import_external_transactions(chain, vec![pending.transaction.into()])
.pop().expect("one result per tx, as in `import_own_transaction`")
}
}
fn local_transactions(&self) -> BTreeMap<H256, pool::local_transactions::Status> { fn local_transactions(&self) -> BTreeMap<H256, pool::local_transactions::Status> {
self.transaction_queue.local_transactions() self.transaction_queue.local_transactions()
} }
@ -1161,6 +1187,7 @@ mod tests {
infinite_pending_block: false, infinite_pending_block: false,
tx_queue_penalization: Penalization::Disabled, tx_queue_penalization: Penalization::Disabled,
tx_queue_strategy: PrioritizationStrategy::GasPriceOnly, tx_queue_strategy: PrioritizationStrategy::GasPriceOnly,
tx_queue_no_unfamiliar_locals: false,
refuse_service_transactions: false, refuse_service_transactions: false,
pool_limits: Default::default(), pool_limits: Default::default(),
pool_verification_options: pool::verifier::Options { pool_verification_options: pool::verifier::Options {
@ -1175,8 +1202,10 @@ mod tests {
) )
} }
const TEST_CHAIN_ID: u64 = 2;
fn transaction() -> SignedTransaction { fn transaction() -> SignedTransaction {
transaction_with_chain_id(2) transaction_with_chain_id(TEST_CHAIN_ID)
} }
fn transaction_with_chain_id(chain_id: u64) -> SignedTransaction { fn transaction_with_chain_id(chain_id: u64) -> SignedTransaction {
@ -1250,6 +1279,53 @@ mod tests {
assert_eq!(miner.ready_transactions(&client, 10, PendingOrdering::Priority).len(), 1); assert_eq!(miner.ready_transactions(&client, 10, PendingOrdering::Priority).len(), 1);
} }
#[test]
fn should_treat_unfamiliar_locals_selectively() {
// given
let keypair = Random.generate().unwrap();
let client = TestBlockChainClient::default();
let account_provider = AccountProvider::transient_provider();
account_provider.insert_account(keypair.secret().clone(), "").expect("can add accounts to the provider we just created");
let miner = Miner::new(
MinerOptions {
tx_queue_no_unfamiliar_locals: true,
..miner().options
},
GasPricer::new_fixed(0u64.into()),
&Spec::new_test(),
Some(Arc::new(account_provider)),
);
let transaction = transaction();
let best_block = 0;
// when
// This transaction should not be marked as local because our account_provider doesn't have the sender
let res = miner.import_claimed_local_transaction(&client, PendingTransaction::new(transaction.clone(), None), false);
// then
// Check the same conditions as `should_import_external_transaction` first. Behaviour should be identical.
// That is: it's treated as though we added it through `import_external_transactions`
assert_eq!(res.unwrap(), ());
assert_eq!(miner.pending_transactions(best_block), None);
assert_eq!(miner.pending_receipts(best_block), None);
assert_eq!(miner.ready_transactions(&client, 10, PendingOrdering::Priority).len(), 0);
assert!(miner.prepare_pending_block(&client));
assert_eq!(miner.ready_transactions(&client, 10, PendingOrdering::Priority).len(), 1);
// when - 2nd part: create a local transaction from account_provider.
// Borrow the transaction used before & sign with our generated keypair.
let local_transaction = transaction.deconstruct().0.as_unsigned().clone().sign(keypair.secret(), Some(TEST_CHAIN_ID));
let res2 = miner.import_claimed_local_transaction(&client, PendingTransaction::new(local_transaction, None), false);
// then - 2nd part: we add on the results from the last pending block.
// This is borrowed from `should_make_pending_block_when_importing_own_transaction` and slightly modified.
assert_eq!(res2.unwrap(), ());
assert_eq!(miner.pending_transactions(best_block).unwrap().len(), 2);
assert_eq!(miner.pending_receipts(best_block).unwrap().len(), 2);
assert_eq!(miner.ready_transactions(&client, 10, PendingOrdering::Priority).len(), 2);
assert!(!miner.prepare_pending_block(&client));
}
#[test] #[test]
fn should_not_seal_unless_enabled() { fn should_not_seal_unless_enabled() {
let miner = miner(); let miner = miner();

View File

@ -138,6 +138,12 @@ pub trait MinerService : Send + Sync {
-> Result<(), transaction::Error> -> Result<(), transaction::Error>
where C: BlockChainClient; where C: BlockChainClient;
/// Imports transactions from potentially external sources, with behaviour determined
/// by the config flag `tx_queue_allow_unfamiliar_locals`
fn import_claimed_local_transaction<C>(&self, chain: &C, transaction: PendingTransaction, trusted: bool)
-> Result<(), transaction::Error>
where C: BlockChainClient;
/// Removes transaction from the pool. /// Removes transaction from the pool.
/// ///
/// Attempts to "cancel" a transaction. If it was not propagated yet (or not accepted by other peers) /// Attempts to "cancel" a transaction. If it was not propagated yet (or not accepted by other peers)

View File

@ -640,6 +640,10 @@ usage! {
"--remove-solved", "--remove-solved",
"Move solved blocks from the work package queue instead of cloning them. This gives a slightly faster import speed, but means that extra solutions submitted for the same work package will go unused.", "Move solved blocks from the work package queue instead of cloning them. This gives a slightly faster import speed, but means that extra solutions submitted for the same work package will go unused.",
FLAG flag_tx_queue_no_unfamiliar_locals: (bool) = false, or |c: &Config| c.mining.as_ref()?.tx_queue_no_unfamiliar_locals.clone(),
"--tx-queue-no-unfamiliar-locals",
"Transactions recieved via local means (RPC, WS, etc) will be treated as external if the sending account is unknown.",
FLAG flag_refuse_service_transactions: (bool) = false, or |c: &Config| c.mining.as_ref()?.refuse_service_transactions.clone(), FLAG flag_refuse_service_transactions: (bool) = false, or |c: &Config| c.mining.as_ref()?.refuse_service_transactions.clone(),
"--refuse-service-transactions", "--refuse-service-transactions",
"Always refuse service transactions.", "Always refuse service transactions.",
@ -1259,6 +1263,7 @@ struct Mining {
tx_queue_strategy: Option<String>, tx_queue_strategy: Option<String>,
tx_queue_ban_count: Option<u16>, tx_queue_ban_count: Option<u16>,
tx_queue_ban_time: Option<u16>, tx_queue_ban_time: Option<u16>,
tx_queue_no_unfamiliar_locals: Option<bool>,
remove_solved: Option<bool>, remove_solved: Option<bool>,
notify_work: Option<Vec<String>>, notify_work: Option<Vec<String>>,
refuse_service_transactions: Option<bool>, refuse_service_transactions: Option<bool>,
@ -1675,6 +1680,7 @@ mod tests {
arg_gas_floor_target: "4700000".into(), arg_gas_floor_target: "4700000".into(),
arg_gas_cap: "6283184".into(), arg_gas_cap: "6283184".into(),
arg_extra_data: Some("Parity".into()), arg_extra_data: Some("Parity".into()),
flag_tx_queue_no_unfamiliar_locals: false,
arg_tx_queue_size: 8192usize, arg_tx_queue_size: 8192usize,
arg_tx_queue_per_sender: None, arg_tx_queue_per_sender: None,
arg_tx_queue_mem_limit: 4u32, arg_tx_queue_mem_limit: 4u32,
@ -1943,6 +1949,7 @@ mod tests {
tx_queue_strategy: None, tx_queue_strategy: None,
tx_queue_ban_count: None, tx_queue_ban_count: None,
tx_queue_ban_time: None, tx_queue_ban_time: None,
tx_queue_no_unfamiliar_locals: None,
tx_gas_limit: None, tx_gas_limit: None,
tx_time_limit: None, tx_time_limit: None,
extra_data: None, extra_data: None,

View File

@ -134,6 +134,7 @@ tx_queue_ban_count = 1
tx_queue_ban_time = 180 #s tx_queue_ban_time = 180 #s
tx_gas_limit = "6283184" tx_gas_limit = "6283184"
tx_time_limit = 100 #ms tx_time_limit = 100 #ms
tx_queue_no_unfamiliar_locals = false
extra_data = "Parity" extra_data = "Parity"
remove_solved = false remove_solved = false
notify_work = ["http://localhost:3001"] notify_work = ["http://localhost:3001"]

View File

@ -550,6 +550,7 @@ impl Configuration {
tx_queue_penalization: to_queue_penalization(self.args.arg_tx_time_limit)?, tx_queue_penalization: to_queue_penalization(self.args.arg_tx_time_limit)?,
tx_queue_strategy: to_queue_strategy(&self.args.arg_tx_queue_strategy)?, tx_queue_strategy: to_queue_strategy(&self.args.arg_tx_queue_strategy)?,
tx_queue_no_unfamiliar_locals: self.args.flag_tx_queue_no_unfamiliar_locals,
refuse_service_transactions: self.args.flag_refuse_service_transactions, refuse_service_transactions: self.args.flag_refuse_service_transactions,
pool_limits: self.pool_limits()?, pool_limits: self.pool_limits()?,

View File

@ -123,10 +123,13 @@ impl<C: miner::BlockChainClient, M: MinerService> FullDispatcher<C, M> {
} }
/// Imports transaction to the miner's queue. /// Imports transaction to the miner's queue.
pub fn dispatch_transaction(client: &C, miner: &M, signed_transaction: PendingTransaction) -> Result<H256> { pub fn dispatch_transaction(client: &C, miner: &M, signed_transaction: PendingTransaction, trusted: bool) -> Result<H256> {
let hash = signed_transaction.transaction.hash(); let hash = signed_transaction.transaction.hash();
miner.import_own_transaction(client, signed_transaction) // use `import_claimed_local_transaction` so we can decide (based on config flags) if we want to treat
// it as local or not. Nodes with public RPC interfaces will want these transactions to be treated like
// external transactions.
miner.import_claimed_local_transaction(client, signed_transaction, trusted)
.map_err(errors::transaction) .map_err(errors::transaction)
.map(|_| hash) .map(|_| hash)
} }
@ -180,7 +183,7 @@ impl<C: miner::BlockChainClient + BlockChainClient, M: MinerService> Dispatcher
} }
fn dispatch_transaction(&self, signed_transaction: PendingTransaction) -> Result<H256> { fn dispatch_transaction(&self, signed_transaction: PendingTransaction) -> Result<H256> {
Self::dispatch_transaction(&*self.client, &*self.miner, signed_transaction) Self::dispatch_transaction(&*self.client, &*self.miner, signed_transaction, true)
} }
} }

View File

@ -828,6 +828,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> Eth for EthClient<
&*self.client, &*self.client,
&*self.miner, &*self.miner,
signed_transaction.into(), signed_transaction.into(),
false
) )
}) })
.map(Into::into) .map(Into::into)

View File

@ -155,7 +155,14 @@ impl MinerService for TestMinerService {
} }
/// Imports transactions to transaction queue. /// Imports transactions to transaction queue.
fn import_own_transaction<C: Nonce + Sync>(&self, chain: &C, pending: PendingTransaction) fn import_own_transaction<C: Nonce + Sync>(&self, _chain: &C, _pending: PendingTransaction)
-> Result<(), transaction::Error> {
// this function is no longer called directly from RPC
unimplemented!();
}
/// Imports transactions to queue - treats as local based on trusted flag, config, and tx source
fn import_claimed_local_transaction<C: Nonce + Sync>(&self, chain: &C, pending: PendingTransaction, _trusted: bool)
-> Result<(), transaction::Error> { -> Result<(), transaction::Error> {
// keep the pending nonces up to date // keep the pending nonces up to date