Merge branch 'master' into jg-test-ui
This commit is contained in:
commit
a133e41e16
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1271,7 +1271,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-ui-precompiled"
|
name = "parity-ui-precompiled"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "git+https://github.com/ethcore/js-precompiled.git#a59b62ecec8773715d1db7e070bbbe5443eb7378"
|
source = "git+https://github.com/ethcore/js-precompiled.git#f5365b857b006ed60c02eb9360d60d7ddef65104"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
@ -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, Rlp, 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)]
|
||||||
@ -68,18 +68,20 @@ pub struct AuthorityRound {
|
|||||||
params: CommonParams,
|
params: CommonParams,
|
||||||
our_params: AuthorityRoundParams,
|
our_params: AuthorityRoundParams,
|
||||||
builtins: BTreeMap<Address, Builtin>,
|
builtins: BTreeMap<Address, Builtin>,
|
||||||
transition_service: IoService<BlockArrived>,
|
transition_service: IoService<()>,
|
||||||
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>>>,
|
||||||
|
password: RwLock<Option<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn header_step(header: &Header) -> Result<usize, ::rlp::DecoderError> {
|
fn header_step(header: &Header) -> Result<usize, ::rlp::DecoderError> {
|
||||||
UntrustedRlp::new(&header.seal()[0]).as_val()
|
UntrustedRlp::new(&header.seal().get(0).expect("was either checked with verify_block_basic or is genesis; has 2 fields; qed (Make sure the spec file has a correct genesis seal)")).as_val()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn header_signature(header: &Header) -> Result<Signature, ::rlp::DecoderError> {
|
fn header_signature(header: &Header) -> Result<Signature, ::rlp::DecoderError> {
|
||||||
UntrustedRlp::new(&header.seal()[1]).as_val::<H520>().map(Into::into)
|
UntrustedRlp::new(&header.seal().get(1).expect("was checked with verify_block_basic; has 2 fields; qed")).as_val::<H520>().map(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
trait AsMillis {
|
trait AsMillis {
|
||||||
@ -101,10 +103,12 @@ impl AuthorityRound {
|
|||||||
params: params,
|
params: params,
|
||||||
our_params: our_params,
|
our_params: our_params,
|
||||||
builtins: builtins,
|
builtins: builtins,
|
||||||
transition_service: try!(IoService::<BlockArrived>::start()),
|
transition_service: try!(IoService::<()>::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),
|
||||||
|
password: RwLock::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)));
|
||||||
@ -143,20 +147,17 @@ struct TransitionHandler {
|
|||||||
engine: Weak<AuthorityRound>,
|
engine: Weak<AuthorityRound>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct BlockArrived;
|
|
||||||
|
|
||||||
const ENGINE_TIMEOUT_TOKEN: TimerToken = 23;
|
const ENGINE_TIMEOUT_TOKEN: TimerToken = 23;
|
||||||
|
|
||||||
impl IoHandler<BlockArrived> for TransitionHandler {
|
impl IoHandler<()> for TransitionHandler {
|
||||||
fn initialize(&self, io: &IoContext<BlockArrived>) {
|
fn initialize(&self, io: &IoContext<()>) {
|
||||||
if let Some(engine) = self.engine.upgrade() {
|
if let Some(engine) = self.engine.upgrade() {
|
||||||
io.register_timer_once(ENGINE_TIMEOUT_TOKEN, engine.remaining_step_duration().as_millis())
|
io.register_timer_once(ENGINE_TIMEOUT_TOKEN, engine.remaining_step_duration().as_millis())
|
||||||
.unwrap_or_else(|e| warn!(target: "poa", "Failed to start consensus step timer: {}.", e))
|
.unwrap_or_else(|e| warn!(target: "poa", "Failed to start consensus step timer: {}.", e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn timeout(&self, io: &IoContext<BlockArrived>, timer: TimerToken) {
|
fn timeout(&self, io: &IoContext<()>, timer: TimerToken) {
|
||||||
if timer == ENGINE_TIMEOUT_TOKEN {
|
if timer == ENGINE_TIMEOUT_TOKEN {
|
||||||
if let Some(engine) = self.engine.upgrade() {
|
if let Some(engine) = self.engine.upgrade() {
|
||||||
engine.step.fetch_add(1, AtomicOrdering::SeqCst);
|
engine.step.fetch_add(1, AtomicOrdering::SeqCst);
|
||||||
@ -208,10 +209,6 @@ impl Engine for AuthorityRound {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply the block reward on finalisation of the block.
|
|
||||||
/// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current).
|
|
||||||
fn on_close_block(&self, _block: &mut ExecutedBlock) {}
|
|
||||||
|
|
||||||
fn is_sealer(&self, author: &Address) -> Option<bool> {
|
fn is_sealer(&self, author: &Address) -> Option<bool> {
|
||||||
let p = &self.our_params;
|
let p = &self.our_params;
|
||||||
Some(p.authorities.contains(author))
|
Some(p.authorities.contains(author))
|
||||||
@ -221,14 +218,14 @@ 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(), self.password.read().clone(), 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);
|
||||||
self.proposed.store(true, AtomicOrdering::SeqCst);
|
self.proposed.store(true, AtomicOrdering::SeqCst);
|
||||||
return Some(vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
return Some(vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||||
@ -303,23 +300,30 @@ 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>) {
|
|
||||||
let mut guard = self.message_channel.lock();
|
|
||||||
*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 is_new_best_block(&self, _best_total_difficulty: U256, best_header: HeaderView, _parent_details: &BlockDetails, new_header: &HeaderView) -> bool {
|
||||||
let new_number = new_header.number();
|
let new_number = new_header.number();
|
||||||
let best_number = best_header.number();
|
let best_number = best_header.number();
|
||||||
if new_number != best_number {
|
if new_number != best_number {
|
||||||
new_number > best_number
|
new_number > best_number
|
||||||
} else {
|
} else {
|
||||||
// Take the oldest step at given height.
|
// Take the oldest step at given height.
|
||||||
let new_step: usize = Rlp::new(&new_header.seal()[0]).as_val();
|
let new_step: usize = Rlp::new(&new_header.seal()[0]).as_val();
|
||||||
let best_step: usize = Rlp::new(&best_header.seal()[0]).as_val();
|
let best_step: usize = Rlp::new(&best_header.seal()[0]).as_val();
|
||||||
new_step < best_step
|
new_step < best_step
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn register_message_channel(&self, message_channel: IoChannel<ClientIoMessage>) {
|
||||||
|
*self.message_channel.lock() = Some(message_channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_signer(&self, _address: Address, password: String) {
|
||||||
|
*self.password.write() = Some(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_account_provider(&self, account_provider: Arc<AccountProvider>) {
|
||||||
|
*self.account_provider.lock() = Some(account_provider);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -387,12 +391,11 @@ mod tests {
|
|||||||
fn generates_seal_and_does_not_double_propose() {
|
fn generates_seal_and_does_not_double_propose() {
|
||||||
let tap = AccountProvider::transient_provider();
|
let tap = AccountProvider::transient_provider();
|
||||||
let addr1 = tap.insert_account("1".sha3(), "1").unwrap();
|
let addr1 = tap.insert_account("1".sha3(), "1").unwrap();
|
||||||
tap.unlock_account_permanently(addr1, "1".into()).unwrap();
|
|
||||||
let addr2 = tap.insert_account("2".sha3(), "2").unwrap();
|
let addr2 = tap.insert_account("2".sha3(), "2").unwrap();
|
||||||
tap.unlock_account_permanently(addr2, "2".into()).unwrap();
|
|
||||||
|
|
||||||
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 +407,18 @@ 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)) {
|
engine.set_signer(addr1, "1".into());
|
||||||
|
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)) {
|
engine.set_signer(addr2, "2".into());
|
||||||
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +58,8 @@ 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>>>,
|
||||||
|
password: RwLock<Option<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BasicAuthority {
|
impl BasicAuthority {
|
||||||
@ -67,6 +69,8 @@ impl BasicAuthority {
|
|||||||
params: params,
|
params: params,
|
||||||
our_params: our_params,
|
our_params: our_params,
|
||||||
builtins: builtins,
|
builtins: builtins,
|
||||||
|
account_provider: Mutex::new(None),
|
||||||
|
password: RwLock::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,13 +102,8 @@ impl Engine for BasicAuthority {
|
|||||||
max(gas_floor_target, gas_limit - gas_limit / bound_divisor + 1.into())
|
max(gas_floor_target, gas_limit - gas_limit / bound_divisor + 1.into())
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// info!("ethash: populate_from_parent #{}: difficulty={} and gas_limit={}", header.number, header.difficulty, header.gas_limit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply the block reward on finalisation of the block.
|
|
||||||
/// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current).
|
|
||||||
fn on_close_block(&self, _block: &mut ExecutedBlock) {}
|
|
||||||
|
|
||||||
fn is_sealer(&self, author: &Address) -> Option<bool> {
|
fn is_sealer(&self, author: &Address) -> Option<bool> {
|
||||||
Some(self.our_params.authorities.contains(author))
|
Some(self.our_params.authorities.contains(author))
|
||||||
}
|
}
|
||||||
@ -113,12 +112,12 @@ 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
|
||||||
if let Ok(signature) = ap.sign(*block.header().author(), None, message) {
|
if let Ok(signature) = ap.sign(*block.header().author(), self.password.read().clone(), message) {
|
||||||
return Some(vec![::rlp::encode(&(&*signature as &[u8])).to_vec()]);
|
return Some(vec![::rlp::encode(&(&*signature as &[u8])).to_vec()]);
|
||||||
} else {
|
} else {
|
||||||
trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable");
|
trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable");
|
||||||
@ -179,6 +178,14 @@ 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 set_signer(&self, _address: Address, password: String) {
|
||||||
|
*self.password.write() = Some(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_account_provider(&self, ap: Arc<AccountProvider>) {
|
||||||
|
*self.account_provider.lock() = Some(ap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -250,10 +257,11 @@ mod tests {
|
|||||||
fn can_generate_seal() {
|
fn can_generate_seal() {
|
||||||
let tap = AccountProvider::transient_provider();
|
let tap = AccountProvider::transient_provider();
|
||||||
let addr = tap.insert_account("".sha3(), "").unwrap();
|
let addr = tap.insert_account("".sha3(), "").unwrap();
|
||||||
tap.unlock_account_permanently(addr, "".into()).unwrap();
|
|
||||||
|
|
||||||
let spec = new_test_authority();
|
let spec = new_test_authority();
|
||||||
let engine = &*spec.engine;
|
let engine = &*spec.engine;
|
||||||
|
engine.set_signer(addr, "".into());
|
||||||
|
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 +269,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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>) {}
|
||||||
}
|
}
|
||||||
|
@ -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_engine_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;
|
||||||
}
|
}
|
||||||
@ -1042,7 +1052,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 +1060,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(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -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_engine_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;
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"transform-runtime",
|
"transform-runtime",
|
||||||
"transform-decorators-legacy",
|
"transform-decorators-legacy",
|
||||||
"transform-class-properties",
|
"transform-class-properties",
|
||||||
|
"transform-object-rest-spread",
|
||||||
"lodash"
|
"lodash"
|
||||||
],
|
],
|
||||||
"retainLines": true,
|
"retainLines": true,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "parity.js",
|
"name": "parity.js",
|
||||||
"version": "0.2.99",
|
"version": "0.2.100",
|
||||||
"main": "release/index.js",
|
"main": "release/index.js",
|
||||||
"jsnext:main": "src/index.js",
|
"jsnext:main": "src/index.js",
|
||||||
"author": "Parity Team <admin@parity.io>",
|
"author": "Parity Team <admin@parity.io>",
|
||||||
@ -48,27 +48,28 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-cli": "6.18.0",
|
"babel-cli": "6.18.0",
|
||||||
"babel-core": "6.18.2",
|
"babel-core": "6.20.0",
|
||||||
"babel-eslint": "7.1.1",
|
"babel-eslint": "7.1.1",
|
||||||
"babel-loader": "6.2.8",
|
"babel-loader": "6.2.8",
|
||||||
"babel-plugin-lodash": "3.2.10",
|
"babel-plugin-lodash": "3.2.10",
|
||||||
"babel-plugin-transform-class-properties": "6.19.0",
|
"babel-plugin-transform-class-properties": "6.18.0",
|
||||||
"babel-plugin-transform-decorators-legacy": "1.3.4",
|
"babel-plugin-transform-decorators-legacy": "1.3.4",
|
||||||
|
"babel-plugin-transform-object-rest-spread": "6.20.2",
|
||||||
"babel-plugin-transform-react-remove-prop-types": "0.2.11",
|
"babel-plugin-transform-react-remove-prop-types": "0.2.11",
|
||||||
"babel-plugin-transform-runtime": "6.15.0",
|
"babel-plugin-transform-runtime": "6.15.0",
|
||||||
"babel-plugin-webpack-alias": "2.1.2",
|
"babel-plugin-webpack-alias": "2.1.2",
|
||||||
"babel-polyfill": "6.16.0",
|
"babel-polyfill": "6.20.0",
|
||||||
"babel-preset-es2015": "6.18.0",
|
"babel-preset-es2015": "6.18.0",
|
||||||
"babel-preset-es2015-rollup": "1.2.0",
|
|
||||||
"babel-preset-es2016": "6.16.0",
|
"babel-preset-es2016": "6.16.0",
|
||||||
"babel-preset-es2017": "6.16.0",
|
"babel-preset-es2017": "6.16.0",
|
||||||
"babel-preset-react": "6.16.0",
|
"babel-preset-react": "6.16.0",
|
||||||
"babel-preset-stage-0": "6.16.0",
|
"babel-preset-stage-0": "6.16.0",
|
||||||
"babel-register": "6.18.0",
|
"babel-register": "6.18.0",
|
||||||
"babel-runtime": "6.18.0",
|
"babel-runtime": "6.20.0",
|
||||||
"chai": "3.5.0",
|
"chai": "3.5.0",
|
||||||
"chai-as-promised": "6.0.0",
|
"chai-as-promised": "6.0.0",
|
||||||
"chai-enzyme": "0.6.1",
|
"chai-enzyme": "0.6.1",
|
||||||
|
"circular-dependency-plugin": "2.0.0",
|
||||||
"copy-webpack-plugin": "4.0.1",
|
"copy-webpack-plugin": "4.0.1",
|
||||||
"core-js": "2.4.1",
|
"core-js": "2.4.1",
|
||||||
"coveralls": "2.11.15",
|
"coveralls": "2.11.15",
|
||||||
|
@ -1 +1 @@
|
|||||||
// test script 4
|
// test script 6
|
||||||
|
@ -262,12 +262,11 @@ export default class Contract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const options = this._getFilterOptions(event, _options);
|
const options = this._getFilterOptions(event, _options);
|
||||||
|
options.fromBlock = 0;
|
||||||
|
options.toBlock = 'latest';
|
||||||
|
|
||||||
return this._api.eth
|
return this._api.eth
|
||||||
.getLogs({
|
.getLogs(options)
|
||||||
fromBlock: 0,
|
|
||||||
toBlock: 'latest',
|
|
||||||
...options
|
|
||||||
})
|
|
||||||
.then((logs) => this.parseEventLogs(logs));
|
.then((logs) => this.parseEventLogs(logs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,8 @@
|
|||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import { MenuItem } from 'material-ui';
|
import { MenuItem } from 'material-ui';
|
||||||
|
|
||||||
import { AddressSelect, Form, Input, InputAddressSelect, Select } from '~/ui';
|
import { AddressSelect, Form, Input, Select, TypedInput } from '~/ui';
|
||||||
|
import { parseAbiType } from '~/util/abi';
|
||||||
|
|
||||||
import styles from '../executeContract.css';
|
import styles from '../executeContract.css';
|
||||||
|
|
||||||
@ -74,7 +75,7 @@ export default class DetailsStep extends Component {
|
|||||||
|
|
||||||
const functions = contract.functions
|
const functions = contract.functions
|
||||||
.filter((func) => !func.constant)
|
.filter((func) => !func.constant)
|
||||||
.sort((a, b) => a.name.localeCompare(b.name))
|
.sort((a, b) => (a.name || '').localeCompare(b.name || ''))
|
||||||
.map((func) => {
|
.map((func) => {
|
||||||
const params = (func.abi.inputs || [])
|
const params = (func.abi.inputs || [])
|
||||||
.map((input, index) => {
|
.map((input, index) => {
|
||||||
@ -125,56 +126,22 @@ export default class DetailsStep extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (func.abi.inputs || []).map((input, index) => {
|
return (func.abi.inputs || []).map((input, index) => {
|
||||||
const onChange = (event, value) => onValueChange(event, index, value);
|
const onChange = (value) => onValueChange(null, index, value);
|
||||||
const onSelect = (event, _index, value) => onValueChange(event, index, value);
|
|
||||||
const onSubmit = (value) => onValueChange(null, index, value);
|
|
||||||
const label = `${input.name}: ${input.type}`;
|
const label = `${input.name}: ${input.type}`;
|
||||||
let inputbox;
|
|
||||||
|
|
||||||
switch (input.type) {
|
|
||||||
case 'address':
|
|
||||||
inputbox = (
|
|
||||||
<InputAddressSelect
|
|
||||||
accounts={ accounts }
|
|
||||||
editing
|
|
||||||
label={ label }
|
|
||||||
value={ values[index] }
|
|
||||||
error={ valuesError[index] }
|
|
||||||
onChange={ onChange } />
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'bool':
|
|
||||||
const boolitems = ['false', 'true'].map((bool) => {
|
|
||||||
return (
|
|
||||||
<MenuItem
|
|
||||||
key={ bool }
|
|
||||||
value={ bool }
|
|
||||||
label={ bool }>{ bool }</MenuItem>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
inputbox = (
|
|
||||||
<Select
|
|
||||||
label={ label }
|
|
||||||
value={ values[index] ? 'true' : 'false' }
|
|
||||||
error={ valuesError[index] }
|
|
||||||
onChange={ onSelect }>{ boolitems }</Select>
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
inputbox = (
|
|
||||||
<Input
|
|
||||||
label={ label }
|
|
||||||
value={ values[index] }
|
|
||||||
error={ valuesError[index] }
|
|
||||||
onSubmit={ onSubmit } />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={ styles.funcparams } key={ index }>
|
<div
|
||||||
{ inputbox }
|
key={ `${index}_${input.name || ''}` }
|
||||||
|
className={ styles.funcparams }
|
||||||
|
>
|
||||||
|
<TypedInput
|
||||||
|
label={ label }
|
||||||
|
value={ values[index] }
|
||||||
|
error={ valuesError[index] }
|
||||||
|
onChange={ onChange }
|
||||||
|
accounts={ accounts }
|
||||||
|
param={ parseAbiType(input.type) }
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -23,6 +23,7 @@ import ContentClear from 'material-ui/svg-icons/content/clear';
|
|||||||
import { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash } from '~/ui';
|
import { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash } from '~/ui';
|
||||||
import { MAX_GAS_ESTIMATION } from '../../util/constants';
|
import { MAX_GAS_ESTIMATION } from '../../util/constants';
|
||||||
import { validateAddress, validateUint } from '../../util/validation';
|
import { validateAddress, validateUint } from '../../util/validation';
|
||||||
|
import { parseAbiType } from '~/util/abi';
|
||||||
|
|
||||||
import DetailsStep from './DetailsStep';
|
import DetailsStep from './DetailsStep';
|
||||||
|
|
||||||
@ -66,7 +67,7 @@ class ExecuteContract extends Component {
|
|||||||
const { contract } = this.props;
|
const { contract } = this.props;
|
||||||
const functions = contract.functions
|
const functions = contract.functions
|
||||||
.filter((func) => !func.constant)
|
.filter((func) => !func.constant)
|
||||||
.sort((a, b) => a.name.localeCompare(b.name));
|
.sort((a, b) => (a.name || '').localeCompare(b.name || ''));
|
||||||
|
|
||||||
this.onFuncChange(null, functions[0]);
|
this.onFuncChange(null, functions[0]);
|
||||||
}
|
}
|
||||||
@ -111,7 +112,7 @@ class ExecuteContract extends Component {
|
|||||||
<Button
|
<Button
|
||||||
key='postTransaction'
|
key='postTransaction'
|
||||||
label='post transaction'
|
label='post transaction'
|
||||||
disabled={ sending || hasError }
|
disabled={ !!(sending || hasError) }
|
||||||
icon={ <IdentityIcon address={ fromAddress } button /> }
|
icon={ <IdentityIcon address={ fromAddress } button /> }
|
||||||
onClick={ this.postTransaction } />
|
onClick={ this.postTransaction } />
|
||||||
];
|
];
|
||||||
@ -174,23 +175,9 @@ class ExecuteContract extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onFuncChange = (event, func) => {
|
onFuncChange = (event, func) => {
|
||||||
const values = func.inputs.map((input) => {
|
const values = (func.abi.inputs || []).map((input) => {
|
||||||
switch (input.kind.type) {
|
const parsedType = parseAbiType(input.type);
|
||||||
case 'address':
|
return parsedType.default;
|
||||||
return '0x';
|
|
||||||
|
|
||||||
case 'bool':
|
|
||||||
return false;
|
|
||||||
|
|
||||||
case 'bytes':
|
|
||||||
return '0x';
|
|
||||||
|
|
||||||
case 'uint':
|
|
||||||
return '0';
|
|
||||||
|
|
||||||
default:
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -92,7 +92,7 @@ function findImports (path) {
|
|||||||
return { error: 'File not found' };
|
return { error: 'File not found' };
|
||||||
}
|
}
|
||||||
|
|
||||||
function compile (data) {
|
function compile (data, optimized = 1) {
|
||||||
const { sourcecode, build } = data;
|
const { sourcecode, build } = data;
|
||||||
const { longVersion } = build;
|
const { longVersion } = build;
|
||||||
|
|
||||||
@ -109,7 +109,7 @@ function compile (data) {
|
|||||||
'': sourcecode
|
'': sourcecode
|
||||||
};
|
};
|
||||||
|
|
||||||
const compiled = compiler.compile({ sources: input }, 0, findImports);
|
const compiled = compiler.compile({ sources: input }, optimized, findImports);
|
||||||
|
|
||||||
self.lastCompile = {
|
self.lastCompile = {
|
||||||
version: longVersion, result: compiled,
|
version: longVersion, result: compiled,
|
||||||
|
@ -58,7 +58,7 @@ function modifyOperation (method, address, owner, operation) {
|
|||||||
contract.instance[method]
|
contract.instance[method]
|
||||||
.estimateGas(options, values)
|
.estimateGas(options, values)
|
||||||
.then((gas) => {
|
.then((gas) => {
|
||||||
options.gas = gas;
|
options.gas = gas.mul(1.2);
|
||||||
return contract.instance[method].postTransaction(options, values);
|
return contract.instance[method].postTransaction(options, values);
|
||||||
})
|
})
|
||||||
.then((requestId) => {
|
.then((requestId) => {
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
import { uniq } from 'lodash';
|
||||||
|
|
||||||
import { Container } from '~/ui';
|
import { Container } from '~/ui';
|
||||||
|
|
||||||
@ -38,7 +39,10 @@ export default class Events extends Component {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const list = events.map((event) => {
|
const eventsKey = uniq(events.map((e) => e.key));
|
||||||
|
const list = eventsKey.map((eventKey) => {
|
||||||
|
const event = events.find((e) => e.key === eventKey);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Event
|
<Event
|
||||||
key={ event.key }
|
key={ event.key }
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
/* Copyright 2015, 2016 Ethcore (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/>.
|
|
||||||
*/
|
|
||||||
/* todo [adgo] - make local */
|
|
||||||
:global .transition-appear {
|
|
||||||
opacity: 0.01;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global .transition-appear.transition-appear-active {
|
|
||||||
opacity: 1;
|
|
||||||
transition: opacity .3s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global .transition-enter {
|
|
||||||
opacity: 0.01;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global .transition-enter.transition-enter-active {
|
|
||||||
opacity: 1;
|
|
||||||
transition: opacity .3s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global .transition-leave {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global .transition-leave.transition-leave-active {
|
|
||||||
opacity: 0.01;
|
|
||||||
transition: opacity .3s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global .absoluteAnimationContainer {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global .absoluteAnimationContainer > .transition-leave {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
// Copyright 2015, 2016 Ethcore (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/>.
|
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import AnimateChildren from './children';
|
|
||||||
|
|
||||||
export default Wrapped => class Animated extends Component {
|
|
||||||
render () {
|
|
||||||
return (
|
|
||||||
<AnimateChildren>
|
|
||||||
<Wrapped { ...this.props } />
|
|
||||||
</AnimateChildren>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,63 +0,0 @@
|
|||||||
// Copyright 2015, 2016 Ethcore (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/>.
|
|
||||||
|
|
||||||
import React, { Component, PropTypes } from 'react';
|
|
||||||
import { isReactComponent } from '../../util/react';
|
|
||||||
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
|
|
||||||
import './AnimateChildren.css';
|
|
||||||
|
|
||||||
export default class AnimateChildren extends Component {
|
|
||||||
render () {
|
|
||||||
const className = this.props.absolute ? 'absoluteAnimationContainer' : '';
|
|
||||||
return (
|
|
||||||
<ReactCSSTransitionGroup
|
|
||||||
component='div'
|
|
||||||
className={ className }
|
|
||||||
transitionName='transition'
|
|
||||||
transitionAppear
|
|
||||||
transitionAppearTimeout={ 0 }
|
|
||||||
transitionLeaveTimeout={ 0 }
|
|
||||||
transitionEnterTimeout={ 0 }
|
|
||||||
>
|
|
||||||
{ this.renderChildren() }
|
|
||||||
</ReactCSSTransitionGroup>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderChildren () {
|
|
||||||
const { children, isView } = this.props;
|
|
||||||
|
|
||||||
if (isView) {
|
|
||||||
return React.cloneElement(this.props.children, {
|
|
||||||
key: this.props.pathname
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isReactComponent(children)) {
|
|
||||||
return React.cloneElement(this.props.children, { ...this.props });
|
|
||||||
}
|
|
||||||
|
|
||||||
return children;
|
|
||||||
}
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
children: PropTypes.any.isRequired,
|
|
||||||
pathname: PropTypes.string,
|
|
||||||
isView: PropTypes.bool,
|
|
||||||
absolute: PropTypes.bool
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
// Copyright 2015, 2016 Ethcore (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/>.
|
|
||||||
|
|
||||||
export default from './Animated';
|
|
@ -15,7 +15,6 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import AnimateChildren from '../../components-compositors/Animated/children';
|
|
||||||
import Call from '../Call';
|
import Call from '../Call';
|
||||||
import CallsToolbar from '../CallsToolbar';
|
import CallsToolbar from '../CallsToolbar';
|
||||||
import styles from './Calls.css';
|
import styles from './Calls.css';
|
||||||
@ -73,13 +72,11 @@ export default class Calls extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimateChildren>
|
<div { ...this._test('empty-wrapper') }>
|
||||||
<div { ...this._test('empty-wrapper') }>
|
<h3 className={ styles.historyInfo } { ...this._test('empty') }>
|
||||||
<h3 className={ styles.historyInfo } { ...this._test('empty') }>
|
Fire up some calls and the results will be here.
|
||||||
Fire up some calls and the results will be here.
|
</h3>
|
||||||
</h3>
|
</div>
|
||||||
</div>
|
|
||||||
</AnimateChildren>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,17 +87,13 @@ export default class Calls extends Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return calls.map((call, idx) => (
|
||||||
<AnimateChildren>
|
<Call
|
||||||
{ calls.map((call, idx) => (
|
key={ calls.length - idx }
|
||||||
<Call
|
call={ call }
|
||||||
key={ calls.length - idx }
|
setActiveCall={ this.setActiveCall }
|
||||||
call={ call }
|
/>
|
||||||
setActiveCall={ this.setActiveCall }
|
));
|
||||||
/>
|
|
||||||
)) }
|
|
||||||
</AnimateChildren>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clearActiveCall = () => {
|
clearActiveCall = () => {
|
||||||
|
@ -23,6 +23,7 @@ const postcssImport = require('postcss-import');
|
|||||||
const postcssNested = require('postcss-nested');
|
const postcssNested = require('postcss-nested');
|
||||||
const postcssVars = require('postcss-simple-vars');
|
const postcssVars = require('postcss-simple-vars');
|
||||||
const rucksack = require('rucksack-css');
|
const rucksack = require('rucksack-css');
|
||||||
|
const CircularDependencyPlugin = require('circular-dependency-plugin');
|
||||||
|
|
||||||
const ENV = process.env.NODE_ENV || 'development';
|
const ENV = process.env.NODE_ENV || 'development';
|
||||||
const isProd = ENV === 'production';
|
const isProd = ENV === 'production';
|
||||||
@ -102,7 +103,12 @@ function getPlugins (_isProd = isProd) {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
new webpack.optimize.OccurrenceOrderPlugin(!_isProd)
|
new webpack.optimize.OccurrenceOrderPlugin(!_isProd),
|
||||||
|
|
||||||
|
new CircularDependencyPlugin({
|
||||||
|
exclude: /node_modules/,
|
||||||
|
failOnError: true
|
||||||
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
if (_isProd) {
|
if (_isProd) {
|
||||||
|
@ -61,6 +61,7 @@ pass = "test_pass"
|
|||||||
|
|
||||||
[mining]
|
[mining]
|
||||||
author = "0xdeadbeefcafe0000000000000000000000000001"
|
author = "0xdeadbeefcafe0000000000000000000000000001"
|
||||||
|
engine_signer = "0xdeadbeefcafe0000000000000000000000000001"
|
||||||
force_sealing = true
|
force_sealing = true
|
||||||
reseal_on_txs = "all"
|
reseal_on_txs = "all"
|
||||||
reseal_min_period = 4000
|
reseal_min_period = 4000
|
||||||
|
@ -40,6 +40,7 @@ pass = "password"
|
|||||||
|
|
||||||
[mining]
|
[mining]
|
||||||
author = "0xdeadbeefcafe0000000000000000000000000001"
|
author = "0xdeadbeefcafe0000000000000000000000000001"
|
||||||
|
engine_signer = "0xdeadbeefcafe0000000000000000000000000001"
|
||||||
force_sealing = true
|
force_sealing = true
|
||||||
reseal_on_txs = "all"
|
reseal_on_txs = "all"
|
||||||
reseal_min_period = 4000
|
reseal_min_period = 4000
|
||||||
|
@ -178,6 +178,8 @@ usage! {
|
|||||||
// -- Sealing/Mining Options
|
// -- Sealing/Mining Options
|
||||||
flag_author: Option<String> = None,
|
flag_author: Option<String> = None,
|
||||||
or |c: &Config| otry!(c.mining).author.clone().map(Some),
|
or |c: &Config| otry!(c.mining).author.clone().map(Some),
|
||||||
|
flag_engine_signer: Option<String> = None,
|
||||||
|
or |c: &Config| otry!(c.mining).engine_signer.clone().map(Some),
|
||||||
flag_force_sealing: bool = false,
|
flag_force_sealing: bool = false,
|
||||||
or |c: &Config| otry!(c.mining).force_sealing.clone(),
|
or |c: &Config| otry!(c.mining).force_sealing.clone(),
|
||||||
flag_reseal_on_txs: String = "own",
|
flag_reseal_on_txs: String = "own",
|
||||||
@ -371,6 +373,7 @@ struct Dapps {
|
|||||||
#[derive(Default, Debug, PartialEq, RustcDecodable)]
|
#[derive(Default, Debug, PartialEq, RustcDecodable)]
|
||||||
struct Mining {
|
struct Mining {
|
||||||
author: Option<String>,
|
author: Option<String>,
|
||||||
|
engine_signer: Option<String>,
|
||||||
force_sealing: Option<bool>,
|
force_sealing: Option<bool>,
|
||||||
reseal_on_txs: Option<String>,
|
reseal_on_txs: Option<String>,
|
||||||
reseal_min_period: Option<u64>,
|
reseal_min_period: Option<u64>,
|
||||||
@ -575,6 +578,7 @@ mod tests {
|
|||||||
|
|
||||||
// -- Sealing/Mining Options
|
// -- Sealing/Mining Options
|
||||||
flag_author: Some("0xdeadbeefcafe0000000000000000000000000001".into()),
|
flag_author: Some("0xdeadbeefcafe0000000000000000000000000001".into()),
|
||||||
|
flag_engine_signer: Some("0xdeadbeefcafe0000000000000000000000000001".into()),
|
||||||
flag_force_sealing: true,
|
flag_force_sealing: true,
|
||||||
flag_reseal_on_txs: "all".into(),
|
flag_reseal_on_txs: "all".into(),
|
||||||
flag_reseal_min_period: 4000u64,
|
flag_reseal_min_period: 4000u64,
|
||||||
@ -746,6 +750,7 @@ mod tests {
|
|||||||
}),
|
}),
|
||||||
mining: Some(Mining {
|
mining: Some(Mining {
|
||||||
author: Some("0xdeadbeefcafe0000000000000000000000000001".into()),
|
author: Some("0xdeadbeefcafe0000000000000000000000000001".into()),
|
||||||
|
engine_signer: Some("0xdeadbeefcafe0000000000000000000000000001".into()),
|
||||||
force_sealing: Some(true),
|
force_sealing: Some(true),
|
||||||
reseal_on_txs: Some("all".into()),
|
reseal_on_txs: Some("all".into()),
|
||||||
reseal_min_period: Some(4000),
|
reseal_min_period: Some(4000),
|
||||||
|
@ -149,6 +149,10 @@ Sealing/Mining Options:
|
|||||||
for sending block rewards from sealed blocks.
|
for sending block rewards from sealed blocks.
|
||||||
NOTE: MINING WILL NOT WORK WITHOUT THIS OPTION.
|
NOTE: MINING WILL NOT WORK WITHOUT THIS OPTION.
|
||||||
(default: {flag_author:?})
|
(default: {flag_author:?})
|
||||||
|
--engine-signer ADDRESS Specify the address which should be used to
|
||||||
|
sign consensus messages and issue blocks.
|
||||||
|
Relevant only to non-PoW chains.
|
||||||
|
(default: {flag_engine_signer:?})
|
||||||
--force-sealing Force the node to author new blocks as if it were
|
--force-sealing Force the node to author new blocks as if it were
|
||||||
always sealing/mining.
|
always sealing/mining.
|
||||||
(default: {flag_force_sealing})
|
(default: {flag_force_sealing})
|
||||||
|
@ -305,6 +305,7 @@ impl Configuration {
|
|||||||
gas_floor_target: try!(to_u256(&self.args.flag_gas_floor_target)),
|
gas_floor_target: try!(to_u256(&self.args.flag_gas_floor_target)),
|
||||||
gas_ceil_target: try!(to_u256(&self.args.flag_gas_cap)),
|
gas_ceil_target: try!(to_u256(&self.args.flag_gas_cap)),
|
||||||
transactions_limit: self.args.flag_tx_queue_size,
|
transactions_limit: self.args.flag_tx_queue_size,
|
||||||
|
engine_signer: try!(self.engine_signer()),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(extras)
|
Ok(extras)
|
||||||
@ -314,6 +315,10 @@ impl Configuration {
|
|||||||
to_address(self.args.flag_etherbase.clone().or(self.args.flag_author.clone()))
|
to_address(self.args.flag_etherbase.clone().or(self.args.flag_author.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn engine_signer(&self) -> Result<Address, String> {
|
||||||
|
to_address(self.args.flag_engine_signer.clone())
|
||||||
|
}
|
||||||
|
|
||||||
fn format(&self) -> Result<Option<DataFormat>, String> {
|
fn format(&self) -> Result<Option<DataFormat>, String> {
|
||||||
match self.args.flag_format {
|
match self.args.flag_format {
|
||||||
Some(ref f) => Ok(Some(try!(f.parse()))),
|
Some(ref f) => Ok(Some(try!(f.parse()))),
|
||||||
|
@ -300,14 +300,14 @@ pub fn password_prompt() -> Result<String, String> {
|
|||||||
|
|
||||||
/// Read a password from password file.
|
/// Read a password from password file.
|
||||||
pub fn password_from_file(path: String) -> Result<String, String> {
|
pub fn password_from_file(path: String) -> Result<String, String> {
|
||||||
let passwords = try!(passwords_from_files(vec![path]));
|
let passwords = try!(passwords_from_files(&[path]));
|
||||||
// use only first password from the file
|
// use only first password from the file
|
||||||
passwords.get(0).map(String::to_owned)
|
passwords.get(0).map(String::to_owned)
|
||||||
.ok_or_else(|| "Password file seems to be empty.".to_owned())
|
.ok_or_else(|| "Password file seems to be empty.".to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads passwords from files. Treats each line as a separate password.
|
/// Reads passwords from files. Treats each line as a separate password.
|
||||||
pub fn passwords_from_files(files: Vec<String>) -> Result<Vec<String>, String> {
|
pub fn passwords_from_files(files: &[String]) -> Result<Vec<String>, String> {
|
||||||
let passwords = files.iter().map(|filename| {
|
let passwords = files.iter().map(|filename| {
|
||||||
let file = try!(File::open(filename).map_err(|_| format!("{} Unable to read password file. Ensure it exists and permissions are correct.", filename)));
|
let file = try!(File::open(filename).map_err(|_| format!("{} Unable to read password file. Ensure it exists and permissions are correct.", filename)));
|
||||||
let reader = BufReader::new(&file);
|
let reader = BufReader::new(&file);
|
||||||
|
@ -204,6 +204,7 @@ pub struct MinerExtras {
|
|||||||
pub gas_floor_target: U256,
|
pub gas_floor_target: U256,
|
||||||
pub gas_ceil_target: U256,
|
pub gas_ceil_target: U256,
|
||||||
pub transactions_limit: usize,
|
pub transactions_limit: usize,
|
||||||
|
pub engine_signer: Address,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MinerExtras {
|
impl Default for MinerExtras {
|
||||||
@ -214,6 +215,7 @@ impl Default for MinerExtras {
|
|||||||
gas_floor_target: U256::from(4_700_000),
|
gas_floor_target: U256::from(4_700_000),
|
||||||
gas_ceil_target: U256::from(6_283_184),
|
gas_ceil_target: U256::from(6_283_184),
|
||||||
transactions_limit: 1024,
|
transactions_limit: 1024,
|
||||||
|
engine_signer: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -207,8 +207,13 @@ pub fn execute(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<(), String> {
|
|||||||
sync_config.warp_sync = cmd.warp_sync;
|
sync_config.warp_sync = cmd.warp_sync;
|
||||||
sync_config.download_old_blocks = cmd.download_old_blocks;
|
sync_config.download_old_blocks = cmd.download_old_blocks;
|
||||||
|
|
||||||
|
let passwords = try!(passwords_from_files(&cmd.acc_conf.password_files));
|
||||||
|
|
||||||
// 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, &passwords)));
|
||||||
|
|
||||||
|
// 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()));
|
||||||
@ -217,6 +222,12 @@ pub fn execute(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<(), String> {
|
|||||||
miner.set_gas_ceil_target(cmd.miner_extras.gas_ceil_target);
|
miner.set_gas_ceil_target(cmd.miner_extras.gas_ceil_target);
|
||||||
miner.set_extra_data(cmd.miner_extras.extra_data);
|
miner.set_extra_data(cmd.miner_extras.extra_data);
|
||||||
miner.set_transactions_limit(cmd.miner_extras.transactions_limit);
|
miner.set_transactions_limit(cmd.miner_extras.transactions_limit);
|
||||||
|
let engine_signer = cmd.miner_extras.engine_signer;
|
||||||
|
if engine_signer != Default::default() {
|
||||||
|
if !passwords.into_iter().any(|p| miner.set_engine_signer(engine_signer, p).is_ok()) {
|
||||||
|
return Err(format!("No password found for the consensus signer {}. Make sure valid password is present in files passed using `--password`.", engine_signer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// create client config
|
// create client config
|
||||||
let mut client_config = to_client_config(
|
let mut client_config = to_client_config(
|
||||||
@ -425,19 +436,17 @@ fn daemonize(_pid_file: String) -> Result<(), String> {
|
|||||||
Err("daemon is no supported on windows".into())
|
Err("daemon is no supported on windows".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_account_provider(dirs: &Directories, cfg: AccountsConfig) -> Result<AccountProvider, String> {
|
fn prepare_account_provider(dirs: &Directories, cfg: AccountsConfig, passwords: &[String]) -> Result<AccountProvider, String> {
|
||||||
use ethcore::ethstore::EthStore;
|
use ethcore::ethstore::EthStore;
|
||||||
use ethcore::ethstore::dir::DiskDirectory;
|
use ethcore::ethstore::dir::DiskDirectory;
|
||||||
|
|
||||||
let passwords = try!(passwords_from_files(cfg.password_files));
|
|
||||||
|
|
||||||
let dir = Box::new(try!(DiskDirectory::create(dirs.keys.clone()).map_err(|e| format!("Could not open keys directory: {}", e))));
|
let dir = Box::new(try!(DiskDirectory::create(dirs.keys.clone()).map_err(|e| format!("Could not open keys directory: {}", e))));
|
||||||
let account_service = AccountProvider::new(Box::new(
|
let account_service = AccountProvider::new(Box::new(
|
||||||
try!(EthStore::open_with_iterations(dir, cfg.iterations).map_err(|e| format!("Could not open keys directory: {}", e)))
|
try!(EthStore::open_with_iterations(dir, cfg.iterations).map_err(|e| format!("Could not open keys directory: {}", e)))
|
||||||
));
|
));
|
||||||
|
|
||||||
for a in cfg.unlocked_accounts {
|
for a in cfg.unlocked_accounts {
|
||||||
if passwords.iter().find(|p| account_service.unlock_account_permanently(a, (*p).clone()).is_ok()).is_none() {
|
if !passwords.iter().any(|p| account_service.unlock_account_permanently(a, (*p).clone()).is_ok()) {
|
||||||
return Err(format!("No password found to unlock account {}. Make sure valid password is present in files passed using `--password`.", a));
|
return Err(format!("No password found to unlock account {}. Make sure valid password is present in files passed using `--password`.", a));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,6 +116,12 @@ impl<C, M, F> ParitySet for ParitySetClient<C, M, F> where
|
|||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_engine_signer(&self, address: H160, password: String) -> Result<bool, Error> {
|
||||||
|
try!(self.active());
|
||||||
|
try!(take_weak!(self.miner).set_engine_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());
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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_engine_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;
|
||||||
}
|
}
|
||||||
|
@ -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_engine_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_setEngineSigner", "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();
|
||||||
|
@ -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_setEngineSigner")]
|
||||||
|
fn set_engine_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>;
|
||||||
|
Loading…
Reference in New Issue
Block a user