add password and AccountProvider

This commit is contained in:
keorn 2016-12-05 17:08:16 +00:00
parent 53b479fb7a
commit 529a7fc33c
13 changed files with 119 additions and 57 deletions

View File

@ -21,13 +21,15 @@ use std::sync::Weak;
use std::time::{UNIX_EPOCH, Duration}; use std::time::{UNIX_EPOCH, Duration};
use util::*; use util::*;
use ethkey::{verify_address, Signature}; use ethkey::{verify_address, Signature};
use rlp::{Rlp, UntrustedRlp, View, encode}; use rlp::{UntrustedRlp, View, encode};
use account_provider::AccountProvider; use account_provider::AccountProvider;
use block::*; use block::*;
use spec::CommonParams; use spec::CommonParams;
use engines::Engine; use engines::Engine;
use header::Header; use header::Header;
use error::{Error, BlockError}; use error::{Error, BlockError};
use blockchain::extras::BlockDetails;
use views::HeaderView;
use evm::Schedule; use evm::Schedule;
use ethjson; use ethjson;
use io::{IoContext, IoHandler, TimerToken, IoService, IoChannel}; use io::{IoContext, IoHandler, TimerToken, IoService, IoChannel};
@ -35,8 +37,6 @@ use service::ClientIoMessage;
use transaction::SignedTransaction; use transaction::SignedTransaction;
use env_info::EnvInfo; use env_info::EnvInfo;
use builtin::Builtin; use builtin::Builtin;
use blockchain::extras::BlockDetails;
use views::HeaderView;
/// `AuthorityRound` params. /// `AuthorityRound` params.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -72,6 +72,7 @@ pub struct AuthorityRound {
message_channel: Mutex<Option<IoChannel<ClientIoMessage>>>, message_channel: Mutex<Option<IoChannel<ClientIoMessage>>>,
step: AtomicUsize, step: AtomicUsize,
proposed: AtomicBool, proposed: AtomicBool,
account_provider: Mutex<Option<Arc<AccountProvider>>>,
} }
fn header_step(header: &Header) -> Result<usize, ::rlp::DecoderError> { fn header_step(header: &Header) -> Result<usize, ::rlp::DecoderError> {
@ -104,7 +105,8 @@ impl AuthorityRound {
transition_service: try!(IoService::<BlockArrived>::start()), transition_service: try!(IoService::<BlockArrived>::start()),
message_channel: Mutex::new(None), message_channel: Mutex::new(None),
step: AtomicUsize::new(initial_step), step: AtomicUsize::new(initial_step),
proposed: AtomicBool::new(false) proposed: AtomicBool::new(false),
account_provider: Mutex::new(None),
}); });
let handler = TransitionHandler { engine: Arc::downgrade(&engine) }; let handler = TransitionHandler { engine: Arc::downgrade(&engine) };
try!(engine.transition_service.register_handler(Arc::new(handler))); try!(engine.transition_service.register_handler(Arc::new(handler)));
@ -196,7 +198,6 @@ impl Engine for AuthorityRound {
} }
fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) { fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) {
header.set_difficulty(parent.difficulty().clone());
header.set_gas_limit({ header.set_gas_limit({
let gas_limit = parent.gas_limit().clone(); let gas_limit = parent.gas_limit().clone();
let bound_divisor = self.our_params.gas_limit_bound_divisor; let bound_divisor = self.our_params.gas_limit_bound_divisor;
@ -221,12 +222,12 @@ impl Engine for AuthorityRound {
/// ///
/// This operation is synchronous and may (quite reasonably) not be available, in which `false` will /// This operation is synchronous and may (quite reasonably) not be available, in which `false` will
/// be returned. /// be returned.
fn generate_seal(&self, block: &ExecutedBlock, accounts: Option<&AccountProvider>) -> Option<Vec<Bytes>> { fn generate_seal(&self, block: &ExecutedBlock) -> Option<Vec<Bytes>> {
if self.proposed.load(AtomicOrdering::SeqCst) { return None; } if self.proposed.load(AtomicOrdering::SeqCst) { return None; }
let header = block.header(); let header = block.header();
let step = self.step(); let step = self.step();
if self.is_step_proposer(step, header.author()) { if self.is_step_proposer(step, header.author()) {
if let Some(ap) = accounts { if let Some(ref ap) = *self.account_provider.lock() {
// Account should be permanently unlocked, otherwise sealing will fail. // Account should be permanently unlocked, otherwise sealing will fail.
if let Ok(signature) = ap.sign(*header.author(), None, header.bare_hash()) { if let Ok(signature) = ap.sign(*header.author(), None, header.bare_hash()) {
trace!(target: "poa", "generate_seal: Issuing a block for step {}.", step); trace!(target: "poa", "generate_seal: Issuing a block for step {}.", step);
@ -303,22 +304,16 @@ impl Engine for AuthorityRound {
t.sender().map(|_|()) // Perform EC recovery and cache sender t.sender().map(|_|()) // Perform EC recovery and cache sender
} }
fn register_message_channel(&self, message_channel: IoChannel<ClientIoMessage>) { fn is_new_best_block(&self, _best_total_difficulty: U256, best_header: HeaderView, _parent_details: &BlockDetails, new_header: &HeaderView) -> bool {
let mut guard = self.message_channel.lock(); new_header.number() > best_header.number()
*guard = Some(message_channel);
} }
fn is_new_best_block(&self, _best_total_difficulty: U256, best_header: HeaderView, _parent_details: &BlockDetails, new_header: &HeaderView) -> bool { fn register_message_channel(&self, message_channel: IoChannel<ClientIoMessage>) {
let new_number = new_header.number(); *self.message_channel.lock() = Some(message_channel);
let best_number = best_header.number(); }
if new_number != best_number {
new_number > best_number fn register_account_provider(&self, account_provider: Arc<AccountProvider>) {
} else { *self.account_provider.lock() = Some(account_provider);
// Take the oldest step at given height.
let new_step: usize = Rlp::new(&new_header.seal()[0]).as_val();
let best_step: usize = Rlp::new(&best_header.seal()[0]).as_val();
new_step < best_step
}
} }
} }
@ -393,6 +388,7 @@ mod tests {
let spec = Spec::new_test_round(); let spec = Spec::new_test_round();
let engine = &*spec.engine; let engine = &*spec.engine;
engine.register_account_provider(Arc::new(tap));
let genesis_header = spec.genesis_header(); let genesis_header = spec.genesis_header();
let mut db1 = get_temp_state_db().take(); let mut db1 = get_temp_state_db().take();
spec.ensure_db_good(&mut db1, &TrieFactory::new(TrieSpec::Secure)).unwrap(); spec.ensure_db_good(&mut db1, &TrieFactory::new(TrieSpec::Secure)).unwrap();
@ -404,16 +400,16 @@ mod tests {
let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes, addr2, (3141562.into(), 31415620.into()), vec![]).unwrap(); let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes, addr2, (3141562.into(), 31415620.into()), vec![]).unwrap();
let b2 = b2.close_and_lock(); let b2 = b2.close_and_lock();
if let Some(seal) = engine.generate_seal(b1.block(), Some(&tap)) { if let Some(seal) = engine.generate_seal(b1.block()) {
assert!(b1.clone().try_seal(engine, seal).is_ok()); assert!(b1.clone().try_seal(engine, seal).is_ok());
// Second proposal is forbidden. // Second proposal is forbidden.
assert!(engine.generate_seal(b1.block(), Some(&tap)).is_none()); assert!(engine.generate_seal(b1.block()).is_none());
} }
if let Some(seal) = engine.generate_seal(b2.block(), Some(&tap)) { if let Some(seal) = engine.generate_seal(b2.block()) {
assert!(b2.clone().try_seal(engine, seal).is_ok()); assert!(b2.clone().try_seal(engine, seal).is_ok());
// Second proposal is forbidden. // Second proposal is forbidden.
assert!(engine.generate_seal(b2.block(), Some(&tap)).is_none()); assert!(engine.generate_seal(b2.block()).is_none());
} }
} }

View File

@ -58,6 +58,7 @@ pub struct BasicAuthority {
params: CommonParams, params: CommonParams,
our_params: BasicAuthorityParams, our_params: BasicAuthorityParams,
builtins: BTreeMap<Address, Builtin>, builtins: BTreeMap<Address, Builtin>,
account_provider: Mutex<Option<Arc<AccountProvider>>>,
} }
impl BasicAuthority { impl BasicAuthority {
@ -67,6 +68,7 @@ impl BasicAuthority {
params: params, params: params,
our_params: our_params, our_params: our_params,
builtins: builtins, builtins: builtins,
account_provider: Mutex::new(None)
} }
} }
} }
@ -113,8 +115,8 @@ impl Engine for BasicAuthority {
/// ///
/// This operation is synchronous and may (quite reasonably) not be available, in which `false` will /// This operation is synchronous and may (quite reasonably) not be available, in which `false` will
/// be returned. /// be returned.
fn generate_seal(&self, block: &ExecutedBlock, accounts: Option<&AccountProvider>) -> Option<Vec<Bytes>> { fn generate_seal(&self, block: &ExecutedBlock) -> Option<Vec<Bytes>> {
if let Some(ap) = accounts { if let Some(ref ap) = *self.account_provider.lock() {
let header = block.header(); let header = block.header();
let message = header.bare_hash(); let message = header.bare_hash();
// account should be pernamently unlocked, otherwise sealing will fail // account should be pernamently unlocked, otherwise sealing will fail
@ -179,6 +181,10 @@ impl Engine for BasicAuthority {
fn verify_transaction(&self, t: &SignedTransaction, _header: &Header) -> Result<(), Error> { fn verify_transaction(&self, t: &SignedTransaction, _header: &Header) -> Result<(), Error> {
t.sender().map(|_|()) // Perform EC recovery and cache sender t.sender().map(|_|()) // Perform EC recovery and cache sender
} }
fn register_account_provider(&self, ap: Arc<AccountProvider>) {
*self.account_provider.lock() = Some(ap);
}
} }
#[cfg(test)] #[cfg(test)]
@ -254,6 +260,7 @@ mod tests {
let spec = new_test_authority(); let spec = new_test_authority();
let engine = &*spec.engine; let engine = &*spec.engine;
engine.register_account_provider(Arc::new(tap));
let genesis_header = spec.genesis_header(); let genesis_header = spec.genesis_header();
let mut db_result = get_temp_state_db(); let mut db_result = get_temp_state_db();
let mut db = db_result.take(); let mut db = db_result.take();
@ -261,7 +268,7 @@ mod tests {
let last_hashes = Arc::new(vec![genesis_header.hash()]); let last_hashes = Arc::new(vec![genesis_header.hash()]);
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap(); let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = b.close_and_lock(); let b = b.close_and_lock();
let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap(); let seal = engine.generate_seal(b.block()).unwrap();
assert!(b.try_seal(engine, seal).is_ok()); assert!(b.try_seal(engine, seal).is_ok());
} }

View File

@ -23,7 +23,6 @@ use spec::CommonParams;
use evm::Schedule; use evm::Schedule;
use block::ExecutedBlock; use block::ExecutedBlock;
use util::Bytes; use util::Bytes;
use account_provider::AccountProvider;
/// An engine which does not provide any consensus mechanism, just seals blocks internally. /// An engine which does not provide any consensus mechanism, just seals blocks internally.
pub struct InstantSeal { pub struct InstantSeal {
@ -60,7 +59,7 @@ impl Engine for InstantSeal {
fn is_sealer(&self, _author: &Address) -> Option<bool> { Some(true) } fn is_sealer(&self, _author: &Address) -> Option<bool> { Some(true) }
fn generate_seal(&self, _block: &ExecutedBlock, _accounts: Option<&AccountProvider>) -> Option<Vec<Bytes>> { fn generate_seal(&self, _block: &ExecutedBlock) -> Option<Vec<Bytes>> {
Some(Vec::new()) Some(Vec::new())
} }
} }
@ -70,16 +69,12 @@ mod tests {
use util::*; use util::*;
use util::trie::TrieSpec; use util::trie::TrieSpec;
use tests::helpers::*; use tests::helpers::*;
use account_provider::AccountProvider;
use spec::Spec; use spec::Spec;
use header::Header; use header::Header;
use block::*; use block::*;
#[test] #[test]
fn instant_can_seal() { fn instant_can_seal() {
let tap = AccountProvider::transient_provider();
let addr = tap.insert_account("".sha3(), "").unwrap();
let spec = Spec::new_instant(); let spec = Spec::new_instant();
let engine = &*spec.engine; let engine = &*spec.engine;
let genesis_header = spec.genesis_header(); let genesis_header = spec.genesis_header();
@ -87,10 +82,9 @@ mod tests {
let mut db = db_result.take(); let mut db = db_result.take();
spec.ensure_db_good(&mut db, &TrieFactory::new(TrieSpec::Secure)).unwrap(); spec.ensure_db_good(&mut db, &TrieFactory::new(TrieSpec::Secure)).unwrap();
let last_hashes = Arc::new(vec![genesis_header.hash()]); let last_hashes = Arc::new(vec![genesis_header.hash()]);
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap(); let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::default(), (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = b.close_and_lock(); let b = b.close_and_lock();
// Seal with empty AccountProvider. let seal = engine.generate_seal(b.block()).unwrap();
let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap();
assert!(b.try_seal(engine, seal).is_ok()); assert!(b.try_seal(engine, seal).is_ok());
} }

View File

@ -94,7 +94,7 @@ pub trait Engine : Sync + Send {
/// ///
/// This operation is synchronous and may (quite reasonably) not be available, in which None will /// This operation is synchronous and may (quite reasonably) not be available, in which None will
/// be returned. /// be returned.
fn generate_seal(&self, _block: &ExecutedBlock, _accounts: Option<&AccountProvider>) -> Option<Vec<Bytes>> { None } fn generate_seal(&self, _block: &ExecutedBlock) -> Option<Vec<Bytes>> { None }
/// Phase 1 quick block verification. Only does checks that are cheap. `block` (the header's full block) /// Phase 1 quick block verification. Only does checks that are cheap. `block` (the header's full block)
/// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import. /// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import.
@ -147,11 +147,17 @@ pub trait Engine : Sync + Send {
self.builtins().get(a).expect("attempted to execute nonexistent builtin").execute(input, output); self.builtins().get(a).expect("attempted to execute nonexistent builtin").execute(input, output);
} }
/// Add a channel for communication with Client which can be used for sealing.
fn register_message_channel(&self, _message_channel: IoChannel<ClientIoMessage>) {}
/// Check if new block should be chosen as the one in chain. /// Check if new block should be chosen as the one in chain.
fn is_new_best_block(&self, best_total_difficulty: U256, _best_header: HeaderView, parent_details: &BlockDetails, new_header: &HeaderView) -> bool { fn is_new_best_block(&self, best_total_difficulty: U256, _best_header: HeaderView, parent_details: &BlockDetails, new_header: &HeaderView) -> bool {
ethash::is_new_best_block(best_total_difficulty, parent_details, new_header) ethash::is_new_best_block(best_total_difficulty, parent_details, new_header)
} }
/// Register an account which signs consensus messages.
fn set_signer(&self, _address: Address, _password: String) {}
/// Add a channel for communication with Client which can be used for sealing.
fn register_message_channel(&self, _message_channel: IoChannel<ClientIoMessage>) {}
/// Add an account provider useful for Engines that sign stuff.
fn register_account_provider(&self, _account_provider: Arc<AccountProvider>) {}
} }

View File

@ -19,7 +19,7 @@ use std::time::{Instant, Duration};
use util::*; use util::*;
use util::using_queue::{UsingQueue, GetAction}; use util::using_queue::{UsingQueue, GetAction};
use account_provider::AccountProvider; use account_provider::{AccountProvider, Error as AccountError};
use views::{BlockView, HeaderView}; use views::{BlockView, HeaderView};
use header::Header; use header::Header;
use state::{State, CleanupMode}; use state::{State, CleanupMode};
@ -464,15 +464,12 @@ impl Miner {
/// Attempts to perform internal sealing (one that does not require work) to return Ok(sealed), /// Attempts to perform internal sealing (one that does not require work) to return Ok(sealed),
/// Err(Some(block)) returns for unsuccesful sealing while Err(None) indicates misspecified engine. /// Err(Some(block)) returns for unsuccesful sealing while Err(None) indicates misspecified engine.
fn seal_block_internally(&self, block: ClosedBlock) -> Result<SealedBlock, Option<ClosedBlock>> { fn seal_block_internally(&self, block: ClosedBlock) -> Result<SealedBlock, Option<ClosedBlock>> {
trace!(target: "miner", "seal_block_internally: block has transaction - attempting internal seal."); trace!(target: "miner", "seal_block_internally: attempting internal seal.");
let s = self.engine.generate_seal(block.block(), match self.accounts { let s = self.engine.generate_seal(block.block());
Some(ref x) => Some(&**x),
None => None,
});
if let Some(seal) = s { if let Some(seal) = s {
trace!(target: "miner", "seal_block_internally: managed internal seal. importing..."); trace!(target: "miner", "seal_block_internally: managed internal seal. importing...");
block.lock().try_seal(&*self.engine, seal).or_else(|_| { block.lock().try_seal(&*self.engine, seal).or_else(|(e, _)| {
warn!("prepare_sealing: ERROR: try_seal failed when given internally generated seal. WTF?"); warn!("prepare_sealing: ERROR: try_seal failed when given internally generated seal: {}", e);
Err(None) Err(None)
}) })
} else { } else {
@ -485,7 +482,7 @@ impl Miner {
fn seal_and_import_block_internally(&self, chain: &MiningBlockChainClient, block: ClosedBlock) -> bool { fn seal_and_import_block_internally(&self, chain: &MiningBlockChainClient, block: ClosedBlock) -> bool {
if !block.transactions().is_empty() || self.forced_sealing() { if !block.transactions().is_empty() || self.forced_sealing() {
if let Ok(sealed) = self.seal_block_internally(block) { if let Ok(sealed) = self.seal_block_internally(block) {
if chain.import_block(sealed.rlp_bytes()).is_ok() { if chain.import_sealed_block(sealed).is_ok() {
trace!(target: "miner", "import_block_internally: imported internally sealed block"); trace!(target: "miner", "import_block_internally: imported internally sealed block");
return true return true
} }
@ -740,6 +737,19 @@ impl MinerService for Miner {
*self.author.write() = author; *self.author.write() = author;
} }
fn set_consensus_signer(&self, address: Address, password: String) -> Result<(), AccountError> {
if self.seals_internally {
if let Some(ref ap) = self.accounts {
try!(ap.sign(address.clone(), Some(password.clone()), Default::default()));
}
let mut sealing_work = self.sealing_work.lock();
sealing_work.enabled = self.engine.is_sealer(&address).unwrap_or(false);
*self.author.write() = address;
self.engine.set_signer(address, password);
}
Ok(())
}
fn set_extra_data(&self, extra_data: Bytes) { fn set_extra_data(&self, extra_data: Bytes) {
*self.extra_data.write() = extra_data; *self.extra_data.write() = extra_data;
} }
@ -1020,6 +1030,11 @@ impl MinerService for Miner {
let (block, original_work_hash) = self.prepare_block(chain); let (block, original_work_hash) = self.prepare_block(chain);
if self.seals_internally { if self.seals_internally {
trace!(target: "miner", "update_sealing: engine indicates internal sealing"); trace!(target: "miner", "update_sealing: engine indicates internal sealing");
{
let mut sealing_work = self.sealing_work.lock();
sealing_work.queue.push(block.clone());
sealing_work.queue.use_last_ref();
}
self.seal_and_import_block_internally(chain, block); self.seal_and_import_block_internally(chain, block);
} else { } else {
trace!(target: "miner", "update_sealing: engine does not seal internally, preparing work"); trace!(target: "miner", "update_sealing: engine does not seal internally, preparing work");
@ -1042,7 +1057,7 @@ impl MinerService for Miner {
ret.map(f) ret.map(f)
} }
fn submit_seal(&self, chain: &MiningBlockChainClient, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error> { fn submit_seal(&self, chain: &MiningBlockChainClient, block_hash: H256, seal: Vec<Bytes>) -> Result<(), Error> {
let result = let result =
if let Some(b) = self.sealing_work.lock().queue.get_used_if( if let Some(b) = self.sealing_work.lock().queue.get_used_if(
if self.options.enable_resubmission { if self.options.enable_resubmission {
@ -1050,22 +1065,22 @@ impl MinerService for Miner {
} else { } else {
GetAction::Take GetAction::Take
}, },
|b| &b.hash() == &pow_hash |b| &b.hash() == &block_hash
) { ) {
trace!(target: "miner", "Sealing block {}={}={} with seal {:?}", pow_hash, b.hash(), b.header().bare_hash(), seal); trace!(target: "miner", "Submitted block {}={}={} with seal {:?}", block_hash, b.hash(), b.header().bare_hash(), seal);
b.lock().try_seal(&*self.engine, seal).or_else(|(e, _)| { b.lock().try_seal(&*self.engine, seal).or_else(|(e, _)| {
warn!(target: "miner", "Mined solution rejected: {}", e); warn!(target: "miner", "Mined solution rejected: {}", e);
Err(Error::PowInvalid) Err(Error::PowInvalid)
}) })
} else { } else {
warn!(target: "miner", "Mined solution rejected: Block unknown or out of date."); warn!(target: "miner", "Submitted solution rejected: Block unknown or out of date.");
Err(Error::PowHashInvalid) Err(Error::PowHashInvalid)
}; };
result.and_then(|sealed| { result.and_then(|sealed| {
let n = sealed.header().number(); let n = sealed.header().number();
let h = sealed.header().hash(); let h = sealed.header().hash();
try!(chain.import_sealed_block(sealed)); try!(chain.import_sealed_block(sealed));
info!(target: "miner", "Mined block imported OK. #{}: {}", Colour::White.bold().paint(format!("{}", n)), Colour::White.bold().paint(h.hex())); info!(target: "miner", "Submitted block imported OK. #{}: {}", Colour::White.bold().paint(format!("{}", n)), Colour::White.bold().paint(h.hex()));
Ok(()) Ok(())
}) })
} }

View File

@ -76,6 +76,9 @@ pub trait MinerService : Send + Sync {
/// Set the author that we will seal blocks as. /// Set the author that we will seal blocks as.
fn set_author(&self, author: Address); fn set_author(&self, author: Address);
/// Set info necessary to sign consensus messages.
fn set_consensus_signer(&self, address: Address, password: String) -> Result<(), ::account_provider::Error>;
/// Get the extra_data that we will seal blocks with. /// Get the extra_data that we will seal blocks with.
fn extra_data(&self) -> Bytes; fn extra_data(&self) -> Bytes;

View File

@ -70,7 +70,7 @@ pub struct RunCmd {
pub http_conf: HttpConfiguration, pub http_conf: HttpConfiguration,
pub ipc_conf: IpcConfiguration, pub ipc_conf: IpcConfiguration,
pub net_conf: NetworkConfiguration, pub net_conf: NetworkConfiguration,
pub network_id: Option<usize>, pub network_id: Option<u64>,
pub warp_sync: bool, pub warp_sync: bool,
pub acc_conf: AccountsConfig, pub acc_conf: AccountsConfig,
pub gas_pricer: GasPricerConfig, pub gas_pricer: GasPricerConfig,
@ -208,6 +208,9 @@ pub fn execute(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<(), String> {
// prepare account provider // prepare account provider
let account_provider = Arc::new(try!(prepare_account_provider(&cmd.dirs, cmd.acc_conf))); let account_provider = Arc::new(try!(prepare_account_provider(&cmd.dirs, cmd.acc_conf)));
// let the Engine access the accounts
spec.engine.register_account_provider(account_provider.clone());
// create miner // create miner
let miner = Miner::new(cmd.miner_options, cmd.gas_pricer.into(), &spec, Some(account_provider.clone())); let miner = Miner::new(cmd.miner_options, cmd.gas_pricer.into(), &spec, Some(account_provider.clone()));
miner.set_author(cmd.miner_extras.author); miner.set_author(cmd.miner_extras.author);

View File

@ -116,6 +116,12 @@ impl<C, M, F> ParitySet for ParitySetClient<C, M, F> where
Ok(true) Ok(true)
} }
fn set_consensus_signer(&self, address: H160, password: String) -> Result<bool, Error> {
try!(self.active());
try!(take_weak!(self.miner).set_consensus_signer(address.into(), password).map_err(Into::into).map_err(errors::from_password_error));
Ok(true)
}
fn set_transactions_limit(&self, limit: usize) -> Result<bool, Error> { fn set_transactions_limit(&self, limit: usize) -> Result<bool, Error> {
try!(self.active()); try!(self.active());

View File

@ -116,6 +116,7 @@ impl EthTester {
fn from_spec(spec: Spec) -> Self { fn from_spec(spec: Spec) -> Self {
let dir = RandomTempPath::new(); let dir = RandomTempPath::new();
let account_provider = account_provider(); let account_provider = account_provider();
spec.engine.register_account_provider(account_provider.clone());
let miner_service = miner_service(&spec, account_provider.clone()); let miner_service = miner_service(&spec, account_provider.clone());
let snapshot_service = snapshot_service(); let snapshot_service = snapshot_service();

View File

@ -25,6 +25,7 @@ use ethcore::header::BlockNumber;
use ethcore::transaction::SignedTransaction; use ethcore::transaction::SignedTransaction;
use ethcore::receipt::{Receipt, RichReceipt}; use ethcore::receipt::{Receipt, RichReceipt};
use ethcore::miner::{MinerService, MinerStatus, TransactionImportResult, LocalTransactionStatus}; use ethcore::miner::{MinerService, MinerStatus, TransactionImportResult, LocalTransactionStatus};
use ethcore::account_provider::Error as AccountError;
/// Test miner service. /// Test miner service.
pub struct TestMinerService { pub struct TestMinerService {
@ -40,6 +41,8 @@ pub struct TestMinerService {
pub pending_receipts: Mutex<BTreeMap<H256, Receipt>>, pub pending_receipts: Mutex<BTreeMap<H256, Receipt>>,
/// Last nonces. /// Last nonces.
pub last_nonces: RwLock<HashMap<Address, U256>>, pub last_nonces: RwLock<HashMap<Address, U256>>,
/// Password held by Engine.
pub password: RwLock<String>,
min_gas_price: RwLock<U256>, min_gas_price: RwLock<U256>,
gas_range_target: RwLock<(U256, U256)>, gas_range_target: RwLock<(U256, U256)>,
@ -61,6 +64,7 @@ impl Default for TestMinerService {
min_gas_price: RwLock::new(U256::from(20_000_000)), min_gas_price: RwLock::new(U256::from(20_000_000)),
gas_range_target: RwLock::new((U256::from(12345), U256::from(54321))), gas_range_target: RwLock::new((U256::from(12345), U256::from(54321))),
author: RwLock::new(Address::zero()), author: RwLock::new(Address::zero()),
password: RwLock::new(String::new()),
extra_data: RwLock::new(vec![1, 2, 3, 4]), extra_data: RwLock::new(vec![1, 2, 3, 4]),
limit: RwLock::new(1024), limit: RwLock::new(1024),
tx_gas_limit: RwLock::new(!U256::zero()), tx_gas_limit: RwLock::new(!U256::zero()),
@ -83,6 +87,12 @@ impl MinerService for TestMinerService {
*self.author.write() = author; *self.author.write() = author;
} }
fn set_consensus_signer(&self, address: Address, password: String) -> Result<(), AccountError> {
*self.author.write() = address;
*self.password.write() = password;
Ok(())
}
fn set_extra_data(&self, extra_data: Bytes) { fn set_extra_data(&self, extra_data: Bytes) {
*self.extra_data.write() = extra_data; *self.extra_data.write() = extra_data;
} }

View File

@ -23,7 +23,7 @@ use ethsync::{SyncProvider, SyncStatus, SyncState, PeerInfo, TransactionStats};
/// TestSyncProvider config. /// TestSyncProvider config.
pub struct Config { pub struct Config {
/// Protocol version. /// Protocol version.
pub network_id: usize, pub network_id: u64,
/// Number of peers. /// Number of peers.
pub num_peers: usize, pub num_peers: usize,
} }

View File

@ -106,6 +106,23 @@ fn rpc_parity_set_author() {
assert_eq!(miner.author(), Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap()); assert_eq!(miner.author(), Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap());
} }
#[test]
fn rpc_parity_set_consensus_signer() {
let miner = miner_service();
let client = client_service();
let network = network_service();
let io = IoHandler::new();
io.add_delegate(parity_set_client(&client, &miner, &network).to_delegate());
let request = r#"{"jsonrpc": "2.0", "method": "parity_setConsensusSigner", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681", "password"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
assert_eq!(miner.author(), Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap());
assert_eq!(*miner.password.read(), "password".to_string());
}
#[test] #[test]
fn rpc_parity_set_transactions_limit() { fn rpc_parity_set_transactions_limit() {
let miner = miner_service(); let miner = miner_service();

View File

@ -44,6 +44,10 @@ build_rpc_trait! {
#[rpc(name = "parity_setAuthor")] #[rpc(name = "parity_setAuthor")]
fn set_author(&self, H160) -> Result<bool, Error>; fn set_author(&self, H160) -> Result<bool, Error>;
/// Sets account for signing consensus messages.
#[rpc(name = "parity_setConsensusSigner")]
fn set_consensus_signer(&self, H160, String) -> Result<bool, Error>;
/// Sets the limits for transaction queue. /// Sets the limits for transaction queue.
#[rpc(name = "parity_setTransactionsLimit")] #[rpc(name = "parity_setTransactionsLimit")]
fn set_transactions_limit(&self, usize) -> Result<bool, Error>; fn set_transactions_limit(&self, usize) -> Result<bool, Error>;