// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of OpenEthereum. // OpenEthereum 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. // OpenEthereum 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 OpenEthereum. If not, see . //! Test implementation of miner service. use std::{ collections::{BTreeMap, BTreeSet, HashMap}, sync::Arc, }; use bytes::Bytes; use ethcore::{ block::SealedBlock, client::{ test_client::TestState, traits::ForceUpdateSealing, EngineInfo, Nonce, PrepareOpenBlock, StateClient, }, engines::{signer::EngineSigner, EthEngine}, error::Error, miner::{self, AuthoringParams, MinerService, TransactionFilter}, }; use ethereum_types::{Address, H256, U256}; use miner::pool::{ local_transactions::Status as LocalTransactionStatus, verifier, QueueStatus, VerifiedTransaction, }; use parking_lot::{Mutex, RwLock}; use types::{ block::Block, header::Header, ids::BlockId, receipt::RichReceipt, transaction::{self, PendingTransaction, SignedTransaction, UnverifiedTransaction}, BlockNumber, }; /// Test miner service. pub struct TestMinerService { /// Imported transactions. pub imported_transactions: Mutex>, /// Pre-existed pending transactions pub pending_transactions: Mutex>, /// Pre-existed local transactions pub local_transactions: Mutex>, /// Pre-existed pending receipts pub pending_receipts: Mutex>, /// Next nonces. pub next_nonces: RwLock>, /// Minimum gas price pub min_gas_price: RwLock>, /// Signer (if any) pub signer: RwLock>>, authoring_params: RwLock, } impl Default for TestMinerService { fn default() -> TestMinerService { TestMinerService { imported_transactions: Default::default(), pending_transactions: Default::default(), local_transactions: Default::default(), pending_receipts: Default::default(), next_nonces: Default::default(), min_gas_price: RwLock::new(Some(0.into())), authoring_params: RwLock::new(AuthoringParams { author: Address::zero(), gas_range_target: (12345.into(), 54321.into()), extra_data: vec![1, 2, 3, 4], }), signer: RwLock::new(None), } } } impl TestMinerService { /// Increments nonce for given address. pub fn increment_nonce(&self, address: &Address) { let mut next_nonces = self.next_nonces.write(); let nonce = next_nonces.entry(*address).or_insert_with(|| 0.into()); *nonce = *nonce + 1; } } impl StateClient for TestMinerService { // State will not be used by test client anyway, since all methods that accept state are mocked type State = TestState; fn latest_state_and_header(&self) -> (Self::State, Header) { (TestState, Header::default()) } fn state_at(&self, _id: BlockId) -> Option { Some(TestState) } } impl EngineInfo for TestMinerService { fn engine(&self) -> &dyn EthEngine { unimplemented!() } } impl MinerService for TestMinerService { type State = TestState; fn pending_state(&self, _latest_block_number: BlockNumber) -> Option { None } fn pending_block_header(&self, _latest_block_number: BlockNumber) -> Option
{ None } fn pending_block(&self, _latest_block_number: BlockNumber) -> Option { None } fn authoring_params(&self) -> AuthoringParams { self.authoring_params.read().clone() } fn set_author>>(&self, author: T) { let author_opt = author.into(); self.authoring_params.write().author = author_opt .as_ref() .map(miner::Author::address) .unwrap_or_default(); match author_opt { Some(miner::Author::Sealer(signer)) => *self.signer.write() = Some(signer), Some(miner::Author::External(_addr)) => (), None => *self.signer.write() = None, } } fn set_extra_data(&self, extra_data: Bytes) { self.authoring_params.write().extra_data = extra_data; } fn set_gas_range_target(&self, target: (U256, U256)) { self.authoring_params.write().gas_range_target = target; } /// Imports transactions to transaction queue. fn import_external_transactions( &self, chain: &C, transactions: Vec, ) -> Vec> { // lets assume that all txs are valid let transactions: Vec<_> = transactions .into_iter() .map(|tx| SignedTransaction::new(tx).unwrap()) .collect(); self.imported_transactions .lock() .extend_from_slice(&transactions); for sender in transactions.iter().map(|tx| tx.sender()) { let nonce = self.next_nonce(chain, &sender); self.next_nonces.write().insert(sender, nonce); } transactions.iter().map(|_| Ok(())).collect() } /// Imports transactions to transaction queue. fn import_own_transaction( &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( &self, chain: &C, pending: PendingTransaction, _trusted: bool, ) -> Result<(), transaction::Error> { // keep the pending nonces up to date let sender = pending.transaction.sender(); let nonce = self.next_nonce(chain, &sender); self.next_nonces.write().insert(sender, nonce); // lets assume that all txs are valid self.imported_transactions.lock().push(pending.transaction); Ok(()) } /// Called when blocks are imported to chain, updates transactions queue. fn chain_new_blocks( &self, _chain: &C, _imported: &[H256], _invalid: &[H256], _enacted: &[H256], _retracted: &[H256], _is_internal: bool, ) { unimplemented!(); } /// New chain head event. Restart mining operation. fn update_sealing(&self, _chain: &C, _force: ForceUpdateSealing) { unimplemented!(); } fn work_package( &self, chain: &C, ) -> Option<(H256, BlockNumber, u64, U256)> { let params = self.authoring_params(); let open_block = chain .prepare_open_block(params.author, params.gas_range_target, params.extra_data) .unwrap(); let closed = open_block.close().unwrap(); let header = &closed.header; Some(( header.hash(), header.number(), header.timestamp(), *header.difficulty(), )) } fn transaction(&self, hash: &H256) -> Option> { self.pending_transactions .lock() .get(hash) .cloned() .map(|tx| Arc::new(VerifiedTransaction::from_pending_block_transaction(tx))) } fn remove_transaction(&self, hash: &H256) -> Option> { self.pending_transactions .lock() .remove(hash) .map(|tx| Arc::new(VerifiedTransaction::from_pending_block_transaction(tx))) } fn pending_transactions(&self, _best_block: BlockNumber) -> Option> { Some(self.pending_transactions.lock().values().cloned().collect()) } fn local_transactions(&self) -> BTreeMap { self.local_transactions .lock() .iter() .map(|(hash, stats)| (*hash, stats.clone())) .collect() } fn ready_transactions_filtered( &self, _chain: &C, _max_len: usize, filter: Option, _ordering: miner::PendingOrdering, ) -> Vec> { match filter { Some(f) => self .queued_transactions() .into_iter() .filter(|tx| f.matches(tx)) .collect(), None => self.queued_transactions(), } } fn pending_transaction_hashes(&self, _chain: &C) -> BTreeSet { self.queued_transactions() .into_iter() .map(|tx| tx.signed().hash()) .collect() } fn queued_transactions(&self) -> Vec> { self.pending_transactions .lock() .values() .cloned() .map(|tx| Arc::new(VerifiedTransaction::from_pending_block_transaction(tx))) .collect() } fn queued_transaction_hashes(&self) -> Vec { self.pending_transactions .lock() .keys() .cloned() .map(|hash| hash) .collect() } fn pending_receipts(&self, _best_block: BlockNumber) -> Option> { Some(self.pending_receipts.lock().clone()) } fn next_nonce(&self, _chain: &C, address: &Address) -> U256 { self.next_nonces .read() .get(address) .cloned() .unwrap_or_default() } fn is_currently_sealing(&self) -> bool { false } fn queue_status(&self) -> QueueStatus { QueueStatus { options: verifier::Options { minimal_gas_price: 0x1312d00.into(), block_gas_limit: 5_000_000.into(), tx_gas_limit: 5_000_000.into(), no_early_reject: false, block_base_fee: None, allow_non_eoa_sender: false, }, status: txpool::LightStatus { mem_usage: 1_000, transaction_count: 52, senders: 1, }, limits: txpool::Options { max_count: 1_024, max_per_sender: 16, max_mem_usage: 5_000, }, } } /// Submit `seal` as a valid solution for the header of `pow_hash`. /// Will check the seal, but not actually insert the block into the chain. fn submit_seal(&self, _pow_hash: H256, _seal: Vec) -> Result { unimplemented!(); } fn sensible_gas_price(&self) -> U256 { 20_000_000_000u64.into() } fn sensible_max_priority_fee(&self) -> U256 { 2_000_000_000u64.into() } fn sensible_gas_limit(&self) -> U256 { 0x5208.into() } fn set_minimal_gas_price(&self, gas_price: U256) -> Result { let mut new_price = self.min_gas_price.write(); match *new_price { Some(ref mut v) => { *v = gas_price; Ok(true) } None => { let error_msg = "Can't update fixed gas price while automatic gas calibration is enabled."; Err(error_msg) } } } }