Backporting to beta (#4418)
* v1.5.1 * Disable notifications (#4243) * Fix wrong token handling (#4254) * Fixing wrong token displayed * Linting * Revert filtering out * Revert the revert * Don't panic on uknown git commit hash (#4231) * Additional logs for own transactions (#4278) * Integration with zgp whitelist contract (#4215) * zgp-transactions checker * polishing * rename + refactor * refuse-service-transactions cl option * fixed tests compilation * Renaming signAndSendTransaction to sendTransaction (#4351) * Fixed deadlock in external_url (#4354) * Fixing web3 in console (#4382) * Fixing estimate gas in case histogram is not available (#4387) * Restarting fetch client every now and then (#4399)
This commit is contained in:
committed by
Gav Wood
parent
b09cfe1885
commit
c7dbd87f8e
@@ -300,22 +300,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().unwrap(), 10_000_000_000_000_000_000u64.into());
|
||||
let res = self.miner.import_external_transactions(self, vec![signed_tx]);
|
||||
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.
|
||||
|
||||
@@ -37,7 +37,6 @@ use bit_set::BitSet;
|
||||
|
||||
use util::*;
|
||||
|
||||
type CodePosition = usize;
|
||||
type ProgramCounter = usize;
|
||||
|
||||
const ONE: U256 = U256([1, 0, 0, 0]);
|
||||
|
||||
@@ -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};
|
||||
@@ -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.
|
||||
|
||||
@@ -117,7 +113,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.
|
||||
@@ -220,22 +216,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 {
|
||||
@@ -265,7 +255,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
|
||||
@@ -281,12 +271,12 @@ mod tests {
|
||||
let banlist1 = txq.ban_sender(tx.sender().unwrap());
|
||||
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().unwrap());
|
||||
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.");
|
||||
@@ -305,12 +295,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.");
|
||||
@@ -327,12 +317,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.");
|
||||
|
||||
@@ -70,36 +70,43 @@ impl LocalTransactionsList {
|
||||
}
|
||||
|
||||
pub fn mark_pending(&mut self, hash: H256) {
|
||||
debug!(target: "own_tx", "Imported to Current (hash {:?})", hash);
|
||||
self.clear_old();
|
||||
self.transactions.insert(hash, Status::Pending);
|
||||
}
|
||||
|
||||
pub fn mark_future(&mut self, hash: H256) {
|
||||
debug!(target: "own_tx", "Imported to Future (hash {:?})", hash);
|
||||
self.transactions.insert(hash, Status::Future);
|
||||
self.clear_old();
|
||||
}
|
||||
|
||||
pub fn mark_rejected(&mut self, tx: SignedTransaction, err: TransactionError) {
|
||||
debug!(target: "own_tx", "Transaction rejected (hash {:?}): {:?}", tx.hash(), err);
|
||||
self.transactions.insert(tx.hash(), Status::Rejected(tx, err));
|
||||
self.clear_old();
|
||||
}
|
||||
|
||||
pub fn mark_replaced(&mut self, tx: SignedTransaction, gas_price: U256, hash: H256) {
|
||||
debug!(target: "own_tx", "Transaction replaced (hash {:?}) by {:?} (new gas price: {:?})", tx.hash(), hash, gas_price);
|
||||
self.transactions.insert(tx.hash(), Status::Replaced(tx, gas_price, hash));
|
||||
self.clear_old();
|
||||
}
|
||||
|
||||
pub fn mark_invalid(&mut self, tx: SignedTransaction) {
|
||||
warn!(target: "own_tx", "Transaction marked invalid (hash {:?})", tx.hash());
|
||||
self.transactions.insert(tx.hash(), Status::Invalid(tx));
|
||||
self.clear_old();
|
||||
}
|
||||
|
||||
pub fn mark_dropped(&mut self, tx: SignedTransaction) {
|
||||
warn!(target: "own_tx", "Transaction dropped (hash {:?})", tx.hash());
|
||||
self.transactions.insert(tx.hash(), Status::Dropped(tx));
|
||||
self.clear_old();
|
||||
}
|
||||
|
||||
pub fn mark_mined(&mut self, tx: SignedTransaction) {
|
||||
info!(target: "own_tx", "Transaction mined (hash {:?})", tx.hash());
|
||||
self.transactions.insert(tx.hash(), Status::Mined(tx));
|
||||
self.clear_old();
|
||||
}
|
||||
|
||||
@@ -30,11 +30,13 @@ use transaction::{Action, SignedTransaction, PendingTransaction};
|
||||
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.
|
||||
@@ -103,8 +105,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 {
|
||||
@@ -123,6 +127,7 @@ impl Default for MinerOptions {
|
||||
work_queue_size: 20,
|
||||
enable_resubmission: true,
|
||||
tx_queue_banning: Banning::Disabled,
|
||||
refuse_service_transactions: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -222,6 +227,7 @@ pub struct Miner {
|
||||
accounts: Option<Arc<AccountProvider>>,
|
||||
work_poster: Option<WorkPoster>,
|
||||
gas_pricer: Mutex<GasPricer>,
|
||||
service_transaction_action: ServiceTransactionAction,
|
||||
}
|
||||
|
||||
impl Miner {
|
||||
@@ -245,6 +251,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()),
|
||||
@@ -264,6 +274,7 @@ impl Miner {
|
||||
engine: spec.engine.clone(),
|
||||
work_poster: work_poster,
|
||||
gas_pricer: Mutex::new(gas_pricer),
|
||||
service_transaction_action: service_transaction_action,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,8 +538,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 {
|
||||
@@ -538,7 +549,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();
|
||||
@@ -556,11 +567,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;
|
||||
@@ -572,54 +583,42 @@ impl Miner {
|
||||
|
||||
fn add_transactions_to_queue(
|
||||
&self,
|
||||
chain: &MiningBlockChainClient,
|
||||
client: &MiningBlockChainClient,
|
||||
transactions: Vec<SignedTransaction>,
|
||||
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),
|
||||
};
|
||||
|
||||
transaction_queue: &mut BanningTransactionQueue,
|
||||
) -> Vec<Result<TransactionImportResult, Error>> {
|
||||
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 insertion_time = client.chain_info().best_block_number;
|
||||
|
||||
transactions.into_iter()
|
||||
.map(|tx| {
|
||||
if chain.transaction_block(TransactionId::Hash(tx.hash())).is_some() {
|
||||
debug!(target: "miner", "Rejected tx {:?}: already in the blockchain", tx.hash());
|
||||
let hash = tx.hash();
|
||||
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));
|
||||
}
|
||||
match self.engine.verify_transaction_basic(&tx, &best_block_header) {
|
||||
Err(e) => {
|
||||
debug!(target: "miner", "Rejected tx {:?} with invalid signature: {:?}", tx.hash(), e);
|
||||
Err(e)
|
||||
},
|
||||
Ok(()) => {
|
||||
let origin = accounts.as_ref().and_then(|accounts| {
|
||||
tx.sender().ok().and_then(|sender| match accounts.contains(&sender) {
|
||||
true => Some(TransactionOrigin::Local),
|
||||
false => None,
|
||||
})
|
||||
}).unwrap_or(default_origin);
|
||||
let origin = accounts.as_ref().and_then(|accounts| {
|
||||
tx.sender().ok().and_then(|sender| match accounts.contains(&sender) {
|
||||
true => Some(TransactionOrigin::Local),
|
||||
false => None,
|
||||
})
|
||||
}).unwrap_or(default_origin);
|
||||
|
||||
match origin {
|
||||
TransactionOrigin::Local | TransactionOrigin::RetractedBlock => {
|
||||
transaction_queue.add(tx, origin, insertion_time, min_block, &fetch_account, &gas_required)
|
||||
},
|
||||
TransactionOrigin::External => {
|
||||
transaction_queue.add_with_banlist(tx, insertion_time, &fetch_account, &gas_required)
|
||||
}
|
||||
}
|
||||
// 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(tx, origin, insertion_time, min_block, &details_provider)
|
||||
},
|
||||
TransactionOrigin::External => {
|
||||
transaction_queue.add_with_banlist(tx, insertion_time, &details_provider)
|
||||
},
|
||||
}
|
||||
})
|
||||
@@ -867,7 +866,6 @@ impl MinerService for Miner {
|
||||
pending: PendingTransaction,
|
||||
) -> Result<TransactionImportResult, Error> {
|
||||
|
||||
let hash = pending.transaction.hash();
|
||||
trace!(target: "own_tx", "Importing transaction: {:?}", pending);
|
||||
|
||||
let imported = {
|
||||
@@ -878,12 +876,10 @@ impl MinerService for Miner {
|
||||
).pop().expect("one result returned per added transaction; one added => one result; qed");
|
||||
|
||||
match import {
|
||||
Ok(ref res) => {
|
||||
trace!(target: "own_tx", "Imported transaction to {:?} (hash: {:?})", res, hash);
|
||||
Ok(_) => {
|
||||
trace!(target: "own_tx", "Status: {:?}", transaction_queue.status());
|
||||
},
|
||||
Err(ref e) => {
|
||||
trace!(target: "own_tx", "Failed to import transaction {:?} (hash: {:?})", e, hash);
|
||||
trace!(target: "own_tx", "Status: {:?}", transaction_queue.status());
|
||||
warn!(target: "own_tx", "Error importing transaction: {:?}", e);
|
||||
},
|
||||
@@ -1165,6 +1161,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 {
|
||||
|
||||
@@ -1229,6 +1279,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(),
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
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().unwrap())
|
||||
} 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()) }))
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user