Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Commit

Permalink
Integration with zgp whitelist contract (#4215)
Browse files Browse the repository at this point in the history
* zgp-transactions checker

* polishing

* rename + refactor

* refuse-service-transactions cl option

* fixed tests compilation
  • Loading branch information
svyatonik authored and gavofyork committed Jan 22, 2017
1 parent 220084d commit 092e24b
Show file tree
Hide file tree
Showing 14 changed files with 1,216 additions and 591 deletions.
15 changes: 11 additions & 4 deletions ethcore/src/client/test_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,22 +308,29 @@ impl TestBlockChainClient {
}
}

/// Inserts a transaction to miners transactions queue.
pub fn insert_transaction_to_queue(&self) {
/// Inserts a transaction with given gas price to miners transactions queue.
pub fn insert_transaction_with_gas_price_to_queue(&self, gas_price: U256) -> H256 {
let keypair = Random.generate().unwrap();
let tx = Transaction {
action: Action::Create,
value: U256::from(100),
data: "3331600055".from_hex().unwrap(),
gas: U256::from(100_000),
gas_price: U256::from(20_000_000_000u64),
gas_price: gas_price,
nonce: U256::zero()
};
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 = res.into_iter().next().unwrap().expect("Successful import");
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.
Expand Down
42 changes: 16 additions & 26 deletions ethcore/src/miner/banning_queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use std::ops::{Deref, DerefMut};
use std::cell::Cell;
use transaction::{SignedTransaction, Action};
use transient_hashmap::TransientHashMap;
use miner::{TransactionQueue, TransactionImportResult, TransactionOrigin, AccountDetails};
use miner::{TransactionQueue, TransactionQueueDetailsProvider, TransactionImportResult, TransactionOrigin};
use miner::transaction_queue::QueuingInstant;
use error::{Error, TransactionError};
use util::{Uint, U256, H256, Address, Hashable};
Expand Down Expand Up @@ -76,16 +76,12 @@ impl BanningTransactionQueue {

/// Add to the queue taking bans into consideration.
/// May reject transaction because of the banlist.
pub fn add_with_banlist<F, G>(
pub fn add_with_banlist(
&mut self,
transaction: SignedTransaction,
time: QueuingInstant,
account_details: &F,
gas_estimator: &G,
) -> Result<TransactionImportResult, Error> where
F: Fn(&Address) -> AccountDetails,
G: Fn(&SignedTransaction) -> U256,
{
details_provider: &TransactionQueueDetailsProvider,
) -> Result<TransactionImportResult, Error> {
if let Threshold::BanAfter(threshold) = self.ban_threshold {
// NOTE In all checks use direct query to avoid increasing ban timeout.

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -219,22 +215,16 @@ mod tests {
use transaction::{Transaction, SignedTransaction, Action};
use error::{Error, TransactionError};
use client::TransactionImportResult;
use miner::{TransactionQueue, TransactionOrigin, AccountDetails};
use miner::{TransactionQueue, TransactionOrigin};
use util::{Uint, U256, Address, FromHex, Hashable};
use miner::transaction_queue::test::DummyTransactionDetailsProvider;

fn queue() -> BanningTransactionQueue {
BanningTransactionQueue::new(TransactionQueue::default(), Threshold::BanAfter(1), Duration::from_secs(180))
}

fn default_account_details(_address: &Address) -> AccountDetails {
AccountDetails {
nonce: U256::zero(),
balance: !U256::zero(),
}
}

fn gas_required(_tx: &SignedTransaction) -> U256 {
0.into()
fn default_tx_provider() -> DummyTransactionDetailsProvider {
DummyTransactionDetailsProvider::default().with_account_nonce(U256::zero())
}

fn transaction(action: Action) -> SignedTransaction {
Expand Down Expand Up @@ -264,7 +254,7 @@ mod tests {
let mut txq = queue();

// 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
// should also deref to queue
Expand All @@ -280,12 +270,12 @@ mod tests {
let banlist1 = txq.ban_sender(tx.sender());
assert!(!banlist1, "Threshold not reached yet.");
// 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);

// when
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
assert!(banlist2, "Threshold should be reached - banned.");
Expand All @@ -304,12 +294,12 @@ mod tests {
let banlist1 = txq.ban_recipient(recipient);
assert!(!banlist1, "Threshold not reached yet.");
// 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);

// when
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
assert!(banlist2, "Threshold should be reached - banned.");
Expand All @@ -326,12 +316,12 @@ mod tests {
let banlist1 = txq.ban_codehash(codehash);
assert!(!banlist1, "Threshold not reached yet.");
// 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);

// when
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
assert!(banlist2, "Threshold should be reached - banned.");
Expand Down
106 changes: 84 additions & 22 deletions ethcore/src/miner/miner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@ use transaction::{Action, UnverifiedTransaction, PendingTransaction, SignedTrans
use receipt::{Receipt, RichReceipt};
use spec::Spec;
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::work_notify::WorkPoster;
use miner::price_info::PriceInfo;
use miner::local_transactions::{Status as LocalTransactionStatus};
use miner::service_transaction_checker::ServiceTransactionChecker;
use header::BlockNumber;

/// Different possible definitions for pending transaction set.
Expand Down Expand Up @@ -102,8 +104,10 @@ pub struct MinerOptions {
pub enable_resubmission: bool,
/// Global gas limit for all transaction in the queue except for local and retracted.
pub tx_queue_gas_limit: GasLimit,
/// Banning settings
/// Banning settings.
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 {
Expand All @@ -122,6 +126,7 @@ impl Default for MinerOptions {
work_queue_size: 20,
enable_resubmission: true,
tx_queue_banning: Banning::Disabled,
refuse_service_transactions: false,
}
}
}
Expand Down Expand Up @@ -221,6 +226,7 @@ pub struct Miner {
accounts: Option<Arc<AccountProvider>>,
work_poster: Option<WorkPoster>,
gas_pricer: Mutex<GasPricer>,
service_transaction_action: ServiceTransactionAction,
}

impl Miner {
Expand All @@ -244,6 +250,10 @@ impl Miner {
ban_duration,
),
};
let service_transaction_action = match options.refuse_service_transactions {
true => ServiceTransactionAction::Refuse,
false => ServiceTransactionAction::Check(ServiceTransactionChecker::default()),
};
Miner {
transaction_queue: Arc::new(Mutex::new(txq)),
next_allowed_reseal: Mutex::new(Instant::now()),
Expand All @@ -263,6 +273,7 @@ impl Miner {
engine: spec.engine.clone(),
work_poster: work_poster,
gas_pricer: Mutex::new(gas_pricer),
service_transaction_action: service_transaction_action,
}
}

Expand Down Expand Up @@ -526,8 +537,8 @@ impl Miner {
}
}

fn update_gas_limit(&self, chain: &MiningBlockChainClient) {
let gas_limit = chain.best_block_header().gas_limit();
fn update_gas_limit(&self, client: &MiningBlockChainClient) {
let gas_limit = client.best_block_header().gas_limit();
let mut queue = self.transaction_queue.lock();
queue.set_gas_limit(gas_limit);
if let GasLimit::Auto = self.options.tx_queue_gas_limit {
Expand All @@ -537,7 +548,7 @@ impl Miner {
}

/// 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");
let prepare_new = {
let mut sealing_work = self.sealing_work.lock();
Expand All @@ -555,11 +566,11 @@ impl Miner {
// | NOTE Code below requires transaction_queue and sealing_work locks. |
// | 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);
}
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 {
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;
Expand All @@ -571,31 +582,23 @@ impl Miner {

fn add_transactions_to_queue(
&self,
chain: &MiningBlockChainClient,
client: &MiningBlockChainClient,
transactions: Vec<UnverifiedTransaction>,
default_origin: TransactionOrigin,
min_block: Option<BlockNumber>,
transaction_queue: &mut BanningTransactionQueue,
) -> 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()
.and_then(|provider| provider.accounts().ok())
.map(|accounts| accounts.into_iter().collect::<HashSet<_>>());

let schedule = chain.latest_schedule();
let gas_required = |tx: &SignedTransaction| tx.gas_required(&schedule).into();
let best_block_header = chain.best_block_header().decode();
let insertion_time = chain.chain_info().best_block_number;
let best_block_header = client.best_block_header().decode();
let insertion_time = client.chain_info().best_block_number;

transactions.into_iter()
.map(|tx| {
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);
return Err(Error::Transaction(TransactionError::AlreadyImported));
}
Expand All @@ -614,13 +617,17 @@ impl Miner {
}
}).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 {
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 => {
transaction_queue.add_with_banlist(transaction, insertion_time, &fetch_account, &gas_required)
}
transaction_queue.add_with_banlist(transaction, insertion_time, &details_provider)
},
}
},
}
Expand Down Expand Up @@ -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)]
mod tests {

Expand Down Expand Up @@ -1222,6 +1283,7 @@ mod tests {
work_queue_size: 5,
enable_resubmission: true,
tx_queue_banning: Banning::Disabled,
refuse_service_transactions: false,
},
GasPricer::new_fixed(0u64.into()),
&Spec::new_test(),
Expand Down
4 changes: 3 additions & 1 deletion ethcore/src/miner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,14 @@ mod external;
mod local_transactions;
mod miner;
mod price_info;
mod service_transaction_checker;
mod transaction_queue;
mod work_notify;

pub use self::external::{ExternalMiner, ExternalMinerService};
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 client::TransactionImportResult;

Expand Down
Loading

0 comments on commit 092e24b

Please sign in to comment.