From 2d907f3322afb90b2c93bedff5c3d5d4553cd08c Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 3 Oct 2016 19:41:00 +0200 Subject: [PATCH 01/97] auto-adjust number of verification threads --- ethcore/src/verification/queue/mod.rs | 164 +++++++++++++++++++++----- 1 file changed, 137 insertions(+), 27 deletions(-) diff --git a/ethcore/src/verification/queue/mod.rs b/ethcore/src/verification/queue/mod.rs index 3f81d53ce..b35b95e2b 100644 --- a/ethcore/src/verification/queue/mod.rs +++ b/ethcore/src/verification/queue/mod.rs @@ -61,6 +61,24 @@ impl Default for Config { } } +struct VerifierHandle { + deleting: Arc, + thread: JoinHandle<()>, +} + +impl VerifierHandle { + // signal to the verifier thread that it should conclude its + // operations. + fn conclude(&self) { + self.deleting.store(true, AtomicOrdering::Release); + } + + // join the verifier thread. + fn join(self) { + self.thread.join().unwrap(); + } +} + /// An item which is in the process of being verified. pub struct Verifying { hash: H256, @@ -90,7 +108,7 @@ pub struct VerificationQueue { engine: Arc, more_to_verify: Arc, verification: Arc>, - verifiers: Vec>, + verifiers: Mutex>, deleting: Arc, ready_signal: Arc, empty: Arc, @@ -157,7 +175,7 @@ impl VerificationQueue { let empty = Arc::new(SCondvar::new()); let panic_handler = PanicHandler::new_in_arc(); - let mut verifiers: Vec> = Vec::new(); + let mut verifiers: Vec = Vec::new(); let thread_count = max(::num_cpus::get(), 3) - 2; for i in 0..thread_count { let verification = verification.clone(); @@ -165,29 +183,30 @@ impl VerificationQueue { let more_to_verify = more_to_verify.clone(); let ready_signal = ready_signal.clone(); let empty = empty.clone(); - let deleting = deleting.clone(); + let deleting = Arc::new(AtomicBool::new(false)); let panic_handler = panic_handler.clone(); - verifiers.push( - thread::Builder::new() - .name(format!("Verifier #{}", i)) - .spawn(move || { - panic_handler.catch_panic(move || { - VerificationQueue::verify(verification, engine, more_to_verify, ready_signal, deleting, empty) - }).unwrap() - }) - .expect("Error starting block verification thread") - ); + verifiers.push(VerifierHandle { + deleting: deleting.clone(), + thread: thread::Builder::new() + .name(format!("Verifier #{}", i)) + .spawn(move || { + panic_handler.catch_panic(move || { + VerificationQueue::verify(verification, engine, more_to_verify, ready_signal, deleting, empty) + }).unwrap() + }) + .expect("Error starting block verification thread") + }); } VerificationQueue { engine: engine, panic_handler: panic_handler, - ready_signal: ready_signal.clone(), - more_to_verify: more_to_verify.clone(), - verification: verification.clone(), - verifiers: verifiers, - deleting: deleting.clone(), + ready_signal: ready_signal, + more_to_verify: more_to_verify, + verification: verification, + verifiers: Mutex::new(verifiers), + deleting: deleting, processing: RwLock::new(HashSet::new()), - empty: empty.clone(), + empty: empty, max_queue_size: max(config.max_queue_size, MIN_QUEUE_LIMIT), max_mem_use: max(config.max_mem_use, MIN_MEM_LIMIT), } @@ -420,14 +439,93 @@ impl VerificationQueue { } } - /// Optimise memory footprint of the heap fields. + /// Optimise memory footprint of the heap fields, and adjust the number of threads + /// to better suit the workload. pub fn collect_garbage(&self) { - { - self.verification.unverified.lock().shrink_to_fit(); + // thresholds for adding and removing verifier threads + // these are unbalanced since having all blocks verified + // is the desirable position. + const ADD_THREAD_THRESHOLD: usize = 10; + const DEL_THREAD_THRESHOLD: usize = 20; + + // TODO: sample over 5 or so ticks. + let (u_len, v_len) = { + let u_len = { + let mut v = self.verification.unverified.lock(); + v.shrink_to_fit(); + v.len() + }; + self.verification.verifying.lock().shrink_to_fit(); - self.verification.verified.lock().shrink_to_fit(); - } + + let v_len = { + let mut v = self.verification.verified.lock(); + v.shrink_to_fit(); + v.len() + }; + + (u_len, v_len) + }; self.processing.write().shrink_to_fit(); + + // more than 10x as many unverified as verified. + if v_len * ADD_THREAD_THRESHOLD < u_len { + self.add_verifier(); + } + + // more than 20x as many verified as unverified. + if u_len * DEL_THREAD_THRESHOLD < v_len { + self.remove_verifier(); + } + } + + // add a verifier thread if possible. + fn add_verifier(&self) { + let mut verifiers = self.verifiers.lock(); + let len = verifiers.len(); + if len == ::num_cpus::get() { + return; + } + + debug!(target: "verification", "Adding verification thread #{}", len); + + let deleting = Arc::new(AtomicBool::new(false)); + let panic_handler = self.panic_handler.clone(); + let verification = self.verification.clone(); + let engine = self.engine.clone(); + let wait = self.more_to_verify.clone(); + let ready = self.ready_signal.clone(); + let empty = self.empty.clone(); + + verifiers.push(VerifierHandle { + deleting: deleting.clone(), + thread: thread::Builder::new() + .name(format!("Verifier #{}", len)) + .spawn(move || { + panic_handler.catch_panic(move || { + VerificationQueue::verify(verification, engine, wait, ready, deleting, empty) + }).unwrap() + }) + .expect("Failed to create verifier thread.") + }); + } + + // remove a verifier thread if possible. + fn remove_verifier(&self) { + let mut verifiers = self.verifiers.lock(); + let len = verifiers.len(); + + if len == 1 { + return; + } + + debug!(target: "verification", "Removing verification thread #{}", len); + + if let Some(handle) = verifiers.pop() { + handle.conclude(); + self.more_to_verify.notify_all(); // to ensure it's joinable immediately. + handle.join(); + } } } @@ -442,10 +540,22 @@ impl Drop for VerificationQueue { trace!(target: "shutdown", "[VerificationQueue] Closing..."); self.clear(); self.deleting.store(true, AtomicOrdering::Release); - self.more_to_verify.notify_all(); - for t in self.verifiers.drain(..) { - t.join().unwrap(); + + let mut verifiers = self.verifiers.lock(); + + // first pass to signal conclusion. must be done before + // notify or deadlock possible. + for handle in verifiers.iter() { + handle.conclude(); } + + self.more_to_verify.notify_all(); + + // second pass to join. + for handle in verifiers.drain(..) { + handle.join(); + } + trace!(target: "shutdown", "[VerificationQueue] Closed."); } } From a7b5dff2520c461ea5873e7b7b250f72ca8344d1 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 3 Oct 2016 19:47:07 +0200 Subject: [PATCH 02/97] ethash unsafety cleanup --- ethcore/src/ethereum/ethash.rs | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 982698a50..1d5d2448e 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use ethash::{quick_get_difficulty, slow_get_seedhash, EthashManager, H256 as EH256}; +use ethash::{quick_get_difficulty, slow_get_seedhash, EthashManager}; use common::*; use block::*; use spec::CommonParams; @@ -182,8 +182,8 @@ impl Engine for Ethash { // Commit state so that we can actually figure out the state root. if let Err(e) = fields.state.commit() { - warn!("Encountered error on state commit: {}", e); - } + warn!("Encountered error on state commit: {}", e); + } } fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> { @@ -202,10 +202,10 @@ impl Engine for Ethash { return Err(From::from(BlockError::DifficultyOutOfBounds(OutOfBounds { min: Some(min_difficulty), max: None, found: header.difficulty().clone() }))) } - let difficulty = Ethash::boundary_to_difficulty(&Ethash::from_ethash(quick_get_difficulty( - &Ethash::to_ethash(header.bare_hash()), + let difficulty = Ethash::boundary_to_difficulty(&H256(quick_get_difficulty( + &header.bare_hash().0, header.nonce().low_u64(), - &Ethash::to_ethash(header.mix_hash()) + &header.mix_hash().0 ))); if &difficulty < header.difficulty() { return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty }))); @@ -230,10 +230,10 @@ impl Engine for Ethash { Mismatch { expected: self.seal_fields(), found: header.seal().len() } ))); } - let result = self.pow.compute_light(header.number() as u64, &Ethash::to_ethash(header.bare_hash()), header.nonce().low_u64()); - let mix = Ethash::from_ethash(result.mix_hash); - let difficulty = Ethash::boundary_to_difficulty(&Ethash::from_ethash(result.value)); - trace!(target: "miner", "num: {}, seed: {}, h: {}, non: {}, mix: {}, res: {}" , header.number() as u64, Ethash::from_ethash(slow_get_seedhash(header.number() as u64)), header.bare_hash(), header.nonce().low_u64(), Ethash::from_ethash(result.mix_hash), Ethash::from_ethash(result.value)); + let result = self.pow.compute_light(header.number() as u64, &header.bare_hash().0, header.nonce().low_u64()); + let mix = H256(result.mix_hash); + let difficulty = Ethash::boundary_to_difficulty(&H256(result.value)); + trace!(target: "miner", "num: {}, seed: {}, h: {}, non: {}, mix: {}, res: {}" , header.number() as u64, H256(slow_get_seedhash(header.number() as u64)), header.bare_hash(), header.nonce().low_u64(), H256(result.mix_hash), H256(result.value)); if mix != header.mix_hash() { return Err(From::from(BlockError::MismatchedH256SealElement(Mismatch { expected: mix, found: header.mix_hash() }))); } @@ -275,7 +275,7 @@ impl Engine for Ethash { } } -#[cfg_attr(feature="dev", allow(wrong_self_convention))] // to_ethash should take self +#[cfg_attr(feature="dev", allow(wrong_self_convention))] impl Ethash { fn calculate_difficulty(&self, header: &Header, parent: &Header) -> U256 { const EXP_DIFF_PERIOD: u64 = 100000; @@ -337,14 +337,6 @@ impl Ethash { (((U256::one() << 255) / *difficulty) << 1).into() } } - - fn to_ethash(hash: H256) -> EH256 { - unsafe { mem::transmute(hash) } - } - - fn from_ethash(hash: EH256) -> H256 { - unsafe { mem::transmute(hash) } - } } impl Header { From 5e382602dd7a18cebcb6bf47ce24aade482d40f5 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 3 Oct 2016 20:09:57 +0200 Subject: [PATCH 03/97] fix logging accuracy --- ethcore/src/verification/queue/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ethcore/src/verification/queue/mod.rs b/ethcore/src/verification/queue/mod.rs index b35b95e2b..3db7135a6 100644 --- a/ethcore/src/verification/queue/mod.rs +++ b/ethcore/src/verification/queue/mod.rs @@ -515,11 +515,12 @@ impl VerificationQueue { let mut verifiers = self.verifiers.lock(); let len = verifiers.len(); + // never remove the last thread. if len == 1 { return; } - debug!(target: "verification", "Removing verification thread #{}", len); + debug!(target: "verification", "Removing verification thread #{}", len + 1); if let Some(handle) = verifiers.pop() { handle.conclude(); From 2d28c703d6a98586df5744e52a1de77aa59501c7 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 3 Oct 2016 20:36:49 +0200 Subject: [PATCH 04/97] reuse add_verifier instrumentation, rolling sample of 5 ticks --- ethcore/src/verification/queue/mod.rs | 53 ++++++++++++++------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/ethcore/src/verification/queue/mod.rs b/ethcore/src/verification/queue/mod.rs index 3db7135a6..ae0555dfa 100644 --- a/ethcore/src/verification/queue/mod.rs +++ b/ethcore/src/verification/queue/mod.rs @@ -113,6 +113,7 @@ pub struct VerificationQueue { ready_signal: Arc, empty: Arc, processing: RwLock>, + rolling_sample: Mutex>, max_queue_size: usize, max_mem_use: usize, } @@ -175,41 +176,27 @@ impl VerificationQueue { let empty = Arc::new(SCondvar::new()); let panic_handler = PanicHandler::new_in_arc(); - let mut verifiers: Vec = Vec::new(); - let thread_count = max(::num_cpus::get(), 3) - 2; - for i in 0..thread_count { - let verification = verification.clone(); - let engine = engine.clone(); - let more_to_verify = more_to_verify.clone(); - let ready_signal = ready_signal.clone(); - let empty = empty.clone(); - let deleting = Arc::new(AtomicBool::new(false)); - let panic_handler = panic_handler.clone(); - verifiers.push(VerifierHandle { - deleting: deleting.clone(), - thread: thread::Builder::new() - .name(format!("Verifier #{}", i)) - .spawn(move || { - panic_handler.catch_panic(move || { - VerificationQueue::verify(verification, engine, more_to_verify, ready_signal, deleting, empty) - }).unwrap() - }) - .expect("Error starting block verification thread") - }); - } - VerificationQueue { + let queue = VerificationQueue { engine: engine, panic_handler: panic_handler, ready_signal: ready_signal, more_to_verify: more_to_verify, verification: verification, - verifiers: Mutex::new(verifiers), + verifiers: Mutex::new(Vec::with_capacity(::num_cpus::get())), deleting: deleting, processing: RwLock::new(HashSet::new()), empty: empty, + rolling_sample: Mutex::new(VecDeque::new()), max_queue_size: max(config.max_queue_size, MIN_QUEUE_LIMIT), max_mem_use: max(config.max_mem_use, MIN_MEM_LIMIT), + }; + + let thread_count = max(::num_cpus::get(), 3) - 2; + for _ in 0..thread_count { + queue.add_verifier(); } + + queue } fn verify(verification: Arc>, engine: Arc, wait: Arc, ready: Arc, deleting: Arc, empty: Arc) { @@ -448,7 +435,10 @@ impl VerificationQueue { const ADD_THREAD_THRESHOLD: usize = 10; const DEL_THREAD_THRESHOLD: usize = 20; - // TODO: sample over 5 or so ticks. + // number of ticks to average queue stats over + // when deciding whether to change the number of verifiers. + const SAMPLE_SIZE: usize = 5; + let (u_len, v_len) = { let u_len = { let mut v = self.verification.unverified.lock(); @@ -468,6 +458,17 @@ impl VerificationQueue { }; self.processing.write().shrink_to_fit(); + let (u_len, v_len) = { + let mut sample = self.rolling_sample.lock(); + sample.push_back((u_len, v_len)); + + if sample.len() > SAMPLE_SIZE { + let _ = sample.pop_front(); + } + + sample.iter().cloned().fold((0, 0), |(u_t, v_t), (u_i, v_i)| (u_t + u_i, v_t + v_i)) + }; + // more than 10x as many unverified as verified. if v_len * ADD_THREAD_THRESHOLD < u_len { self.add_verifier(); @@ -520,7 +521,7 @@ impl VerificationQueue { return; } - debug!(target: "verification", "Removing verification thread #{}", len + 1); + debug!(target: "verification", "Removing verification thread #{}", len - 1); if let Some(handle) = verifiers.pop() { handle.conclude(); From abbf3b3c5840ba18872cd7ac442f7cf39def4a0a Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 4 Oct 2016 20:09:54 +0200 Subject: [PATCH 05/97] verification-rate based thread scaling --- ethcore/src/verification/queue/mod.rs | 89 +++++++++++++++------------ 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/ethcore/src/verification/queue/mod.rs b/ethcore/src/verification/queue/mod.rs index ae0555dfa..ac0dbf592 100644 --- a/ethcore/src/verification/queue/mod.rs +++ b/ethcore/src/verification/queue/mod.rs @@ -18,7 +18,7 @@ //! Sorts them ready for blockchain insertion. use std::thread::{JoinHandle, self}; -use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering}; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrdering}; use std::sync::{Condvar as SCondvar, Mutex as SMutex}; use util::*; use io::*; @@ -113,7 +113,7 @@ pub struct VerificationQueue { ready_signal: Arc, empty: Arc, processing: RwLock>, - rolling_sample: Mutex>, + ticks_since_adjustment: AtomicUsize, max_queue_size: usize, max_mem_use: usize, } @@ -152,6 +152,8 @@ struct Verification { bad: Mutex>, more_to_verify: SMutex<()>, empty: SMutex<()>, + verified_count: AtomicUsize, + drained: AtomicUsize, } impl VerificationQueue { @@ -164,7 +166,8 @@ impl VerificationQueue { bad: Mutex::new(HashSet::new()), more_to_verify: SMutex::new(()), empty: SMutex::new(()), - + verified_count: AtomicUsize::new(0), + drained: AtomicUsize::new(0), }); let more_to_verify = Arc::new(SCondvar::new()); let deleting = Arc::new(AtomicBool::new(false)); @@ -186,7 +189,7 @@ impl VerificationQueue { deleting: deleting, processing: RwLock::new(HashSet::new()), empty: empty, - rolling_sample: Mutex::new(VecDeque::new()), + ticks_since_adjustment: AtomicUsize::new(0), max_queue_size: max(config.max_queue_size, MIN_QUEUE_LIMIT), max_mem_use: max(config.max_mem_use, MIN_MEM_LIMIT), }; @@ -248,7 +251,7 @@ impl VerificationQueue { // we're next! let mut verified = verification.verified.lock(); let mut bad = verification.bad.lock(); - VerificationQueue::drain_verifying(&mut verifying, &mut verified, &mut bad); + VerificationQueue::drain_verifying(&mut verifying, &mut verified, &mut bad, &verification.verified_count); ready.set(); } }, @@ -261,7 +264,7 @@ impl VerificationQueue { verifying.retain(|e| e.hash != hash); if verifying.front().map_or(false, |x| x.output.is_some()) { - VerificationQueue::drain_verifying(&mut verifying, &mut verified, &mut bad); + VerificationQueue::drain_verifying(&mut verifying, &mut verified, &mut bad, &verification.verified_count); ready.set(); } } @@ -269,7 +272,13 @@ impl VerificationQueue { } } - fn drain_verifying(verifying: &mut VecDeque>, verified: &mut VecDeque, bad: &mut HashSet) { + fn drain_verifying( + verifying: &mut VecDeque>, + verified: &mut VecDeque, + bad: &mut HashSet, + v_count: &AtomicUsize + ) { + let start_len = verified.len(); while let Some(output) = verifying.front_mut().and_then(|x| x.output.take()) { assert!(verifying.pop_front().is_some()); @@ -279,6 +288,8 @@ impl VerificationQueue { verified.push_back(output); } } + + v_count.fetch_add(verified.len() - start_len, AtomicOrdering::AcqRel); } /// Clear the queue and stop verification activity. @@ -389,6 +400,8 @@ impl VerificationQueue { let count = min(max, verified.len()); let result = verified.drain(..count).collect::>(); + self.verification.drained.fetch_add(count, AtomicOrdering::AcqRel); + self.ready_signal.reset(); if !verified.is_empty() { self.ready_signal.set(); @@ -429,53 +442,49 @@ impl VerificationQueue { /// Optimise memory footprint of the heap fields, and adjust the number of threads /// to better suit the workload. pub fn collect_garbage(&self) { - // thresholds for adding and removing verifier threads - // these are unbalanced since having all blocks verified - // is the desirable position. - const ADD_THREAD_THRESHOLD: usize = 10; - const DEL_THREAD_THRESHOLD: usize = 20; - // number of ticks to average queue stats over // when deciding whether to change the number of verifiers. - const SAMPLE_SIZE: usize = 5; - - let (u_len, v_len) = { - let u_len = { - let mut v = self.verification.unverified.lock(); - v.shrink_to_fit(); - v.len() - }; + const READJUSTMENT_PERIOD: usize = 5; + { + self.verification.unverified.lock().shrink_to_fit(); self.verification.verifying.lock().shrink_to_fit(); + self.verification.verified.lock().shrink_to_fit(); + } - let v_len = { - let mut v = self.verification.verified.lock(); - v.shrink_to_fit(); - v.len() - }; - - (u_len, v_len) - }; self.processing.write().shrink_to_fit(); - let (u_len, v_len) = { - let mut sample = self.rolling_sample.lock(); - sample.push_back((u_len, v_len)); + if self.ticks_since_adjustment.load(AtomicOrdering::SeqCst) == READJUSTMENT_PERIOD { + self.ticks_since_adjustment.store(0, AtomicOrdering::SeqCst); + } else { + self.ticks_since_adjustment.fetch_add(1, AtomicOrdering::SeqCst); + return; + } - if sample.len() > SAMPLE_SIZE { - let _ = sample.pop_front(); - } + let v_count = self.verification.verified_count.load(AtomicOrdering::Acquire); + let drained = self.verification.drained.load(AtomicOrdering::Acquire); - sample.iter().cloned().fold((0, 0), |(u_t, v_t), (u_i, v_i)| (u_t + u_i, v_t + v_i)) + self.verification.verified_count.store(0, AtomicOrdering::Release); + self.verification.drained.store(0, AtomicOrdering::Release); + + // compute the average rate of verification per thread and determine + // how many are necessary to match the rate of draining. + let num_verifiers = self.verifiers.lock().len(); + let v_count_per = v_count as f64 / num_verifiers as f64; + let needed = if v_count < 20 { + 1 + } else { + (drained as f64 / v_count_per as f64).ceil() as usize }; - // more than 10x as many unverified as verified. - if v_len * ADD_THREAD_THRESHOLD < u_len { + trace!(target: "verification", "v_rate_per={}, drained={}, scaling to {} verifiers", + v_count_per, drained, needed); + + for _ in num_verifiers..needed { self.add_verifier(); } - // more than 20x as many verified as unverified. - if u_len * DEL_THREAD_THRESHOLD < v_len { + for _ in needed..num_verifiers { self.remove_verifier(); } } From 8a5576d133a1c770bd4717235f77ba6aae0aafd2 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 5 Oct 2016 12:10:28 +0200 Subject: [PATCH 06/97] balance rates of draining and importing --- ethcore/src/verification/queue/mod.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/ethcore/src/verification/queue/mod.rs b/ethcore/src/verification/queue/mod.rs index ac0dbf592..8c6b1e5ca 100644 --- a/ethcore/src/verification/queue/mod.rs +++ b/ethcore/src/verification/queue/mod.rs @@ -154,6 +154,7 @@ struct Verification { empty: SMutex<()>, verified_count: AtomicUsize, drained: AtomicUsize, + imported: AtomicUsize, } impl VerificationQueue { @@ -168,6 +169,7 @@ impl VerificationQueue { empty: SMutex::new(()), verified_count: AtomicUsize::new(0), drained: AtomicUsize::new(0), + imported: AtomicUsize::new(0), }); let more_to_verify = Arc::new(SCondvar::new()); let deleting = Arc::new(AtomicBool::new(false)); @@ -345,6 +347,7 @@ impl VerificationQueue { Ok(item) => { self.processing.write().insert(h.clone()); self.verification.unverified.lock().push_back(item); + self.verification.imported.fetch_add(1, AtomicOrdering::AcqRel); self.more_to_verify.notify_all(); Ok(h) }, @@ -463,9 +466,14 @@ impl VerificationQueue { let v_count = self.verification.verified_count.load(AtomicOrdering::Acquire); let drained = self.verification.drained.load(AtomicOrdering::Acquire); + let imported = self.verification.imported.load(AtomicOrdering::Acquire); self.verification.verified_count.store(0, AtomicOrdering::Release); self.verification.drained.store(0, AtomicOrdering::Release); + self.verification.imported.store(0, AtomicOrdering::Release); + + // select which side of the queue is the bottleneck. + let target = min(drained, imported); // compute the average rate of verification per thread and determine // how many are necessary to match the rate of draining. @@ -474,11 +482,11 @@ impl VerificationQueue { let needed = if v_count < 20 { 1 } else { - (drained as f64 / v_count_per as f64).ceil() as usize + (target as f64 / v_count_per as f64).ceil() as usize }; - trace!(target: "verification", "v_rate_per={}, drained={}, scaling to {} verifiers", - v_count_per, drained, needed); + trace!(target: "verification", "v_rate_per={}, target={}, scaling to {} verifiers", + v_count_per, target, needed); for _ in num_verifiers..needed { self.add_verifier(); From 44dcd6bc3b56ae80dc8ef6a9a0d81c4d7bb438dd Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 17 Nov 2016 13:10:33 +0100 Subject: [PATCH 07/97] increase readjustment period --- ethcore/src/verification/queue/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethcore/src/verification/queue/mod.rs b/ethcore/src/verification/queue/mod.rs index 2376215f0..23f82b730 100644 --- a/ethcore/src/verification/queue/mod.rs +++ b/ethcore/src/verification/queue/mod.rs @@ -518,7 +518,7 @@ impl VerificationQueue { pub fn collect_garbage(&self) { // number of ticks to average queue stats over // when deciding whether to change the number of verifiers. - const READJUSTMENT_PERIOD: usize = 5; + const READJUSTMENT_PERIOD: usize = 12; { self.verification.unverified.lock().shrink_to_fit(); From 546cd0065925279c38d82220d1d6a2de0dc21214 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 17 Nov 2016 16:00:23 +0100 Subject: [PATCH 08/97] allocate verifiers up front, hibernate when not needed --- ethcore/src/verification/queue/mod.rs | 166 ++++++++++++++++---------- 1 file changed, 103 insertions(+), 63 deletions(-) diff --git a/ethcore/src/verification/queue/mod.rs b/ethcore/src/verification/queue/mod.rs index 23f82b730..f4a9287d6 100644 --- a/ethcore/src/verification/queue/mod.rs +++ b/ethcore/src/verification/queue/mod.rs @@ -35,6 +35,9 @@ pub mod kind; const MIN_MEM_LIMIT: usize = 16384; const MIN_QUEUE_LIMIT: usize = 512; +// maximum possible number of verification threads. +const MAX_VERIFIERS: usize = 8; + /// Type alias for block queue convenience. pub type BlockQueue = VerificationQueue; @@ -63,13 +66,26 @@ impl Default for Config { struct VerifierHandle { deleting: Arc, + sleep: Arc<(Mutex, Condvar)>, thread: JoinHandle<()>, } impl VerifierHandle { + // signal to the verifier thread that it should sleep. + fn sleep(&self) { + *self.sleep.0.lock() = true; + } + + // signal to the verifier thread that it should wake up. + fn wake_up(&self) { + *self.sleep.0.lock() = false; + self.sleep.1.notify_all(); + } + // signal to the verifier thread that it should conclude its // operations. fn conclude(&self) { + self.wake_up(); self.deleting.store(true, AtomicOrdering::Release); } @@ -115,7 +131,7 @@ pub struct VerificationQueue { engine: Arc, more_to_verify: Arc, verification: Arc>, - verifiers: Mutex>, + verifiers: Mutex<(Vec, usize)>, deleting: Arc, ready_signal: Arc, empty: Arc, @@ -212,31 +228,83 @@ impl VerificationQueue { let empty = Arc::new(SCondvar::new()); let panic_handler = PanicHandler::new_in_arc(); - let queue = VerificationQueue { + let max_verifiers = min(::num_cpus::get(), MAX_VERIFIERS); + let default_amount = max(::num_cpus::get(), 3) - 2; + let mut verifiers = Vec::with_capacity(max_verifiers); + + debug!(target: "verification", "Allocating {} verifiers, {} initially active", max_verifiers, default_amount); + + for i in 0..max_verifiers { + debug!(target: "verification", "Adding verification thread #{}", i); + + let deleting = deleting.clone(); + let panic_handler = panic_handler.clone(); + let verification = verification.clone(); + let engine = engine.clone(); + let wait = more_to_verify.clone(); + let ready = ready_signal.clone(); + let empty = empty.clone(); + + // enable only the first few verifiers. + let sleep = if i < default_amount { + Arc::new((Mutex::new(false), Condvar::new())) + } else { + Arc::new((Mutex::new(true), Condvar::new())) + }; + + verifiers.push(VerifierHandle { + deleting: deleting.clone(), + sleep: sleep.clone(), + thread: thread::Builder::new() + .name(format!("Verifier #{}", i)) + .spawn(move || { + panic_handler.catch_panic(move || { + VerificationQueue::verify(verification, engine, wait, ready, deleting, empty, sleep) + }).unwrap() + }) + .expect("Failed to create verifier thread.") + }); + } + + VerificationQueue { engine: engine, panic_handler: panic_handler, ready_signal: ready_signal, more_to_verify: more_to_verify, verification: verification, - verifiers: Mutex::new(Vec::with_capacity(::num_cpus::get())), + verifiers: Mutex::new((verifiers, default_amount)), deleting: deleting, processing: RwLock::new(HashSet::new()), empty: empty, ticks_since_adjustment: AtomicUsize::new(0), max_queue_size: max(config.max_queue_size, MIN_QUEUE_LIMIT), max_mem_use: max(config.max_mem_use, MIN_MEM_LIMIT), - }; - - let thread_count = max(::num_cpus::get(), 3) - 2; - for _ in 0..thread_count { - queue.add_verifier(); } - - queue } - fn verify(verification: Arc>, engine: Arc, wait: Arc, ready: Arc, deleting: Arc, empty: Arc) { + fn verify( + verification: Arc>, + engine: Arc, + wait: Arc, + ready: Arc, + deleting: Arc, + empty: Arc, + sleep: Arc<(Mutex, Condvar)>, + ) { while !deleting.load(AtomicOrdering::Acquire) { + { + let mut should_sleep = sleep.0.lock(); + while *should_sleep { + trace!(target: "verification", "Verifier sleeping"); + sleep.1.wait(&mut should_sleep); + trace!(target: "verification", "Verifier waking up"); + + if deleting.load(AtomicOrdering::Acquire) { + return; + } + } + } + { let mut more_to_verify = verification.more_to_verify.lock().unwrap(); @@ -548,7 +616,7 @@ impl VerificationQueue { // compute the average rate of verification per thread and determine // how many are necessary to match the rate of draining. - let num_verifiers = self.verifiers.lock().len(); + let num_verifiers = self.verifiers.lock().1; let v_count_per = v_count as f64 / num_verifiers as f64; let needed = if v_count < 20 { 1 @@ -559,63 +627,34 @@ impl VerificationQueue { trace!(target: "verification", "v_rate_per={}, target={}, scaling to {} verifiers", v_count_per, target, needed); - for _ in num_verifiers..needed { - self.add_verifier(); - } - - for _ in needed..num_verifiers { - self.remove_verifier(); - } + self.scale_verifiers(needed); } - // add a verifier thread if possible. - fn add_verifier(&self) { + // wake up or sleep verifiers to get as close to the target as + // possible, never going over the amount of initially allocated threads + // or below 1. + fn scale_verifiers(&self, target: usize) { let mut verifiers = self.verifiers.lock(); - let len = verifiers.len(); - if len == ::num_cpus::get() { - return; + let &mut (ref mut verifiers, ref mut verifier_count) = &mut *verifiers; + + let target = min(verifiers.capacity(), target); + let target = max(1, target); + + debug!(target: "verification", "Scaling from {} to {} verifiers", verifier_count, target); + + // scaling up + for i in *verifier_count..target { + debug!(target: "verification", "Waking up verifier {}", i); + verifiers[i].wake_up(); } - debug!(target: "verification", "Adding verification thread #{}", len); - - let deleting = Arc::new(AtomicBool::new(false)); - let panic_handler = self.panic_handler.clone(); - let verification = self.verification.clone(); - let engine = self.engine.clone(); - let wait = self.more_to_verify.clone(); - let ready = self.ready_signal.clone(); - let empty = self.empty.clone(); - - verifiers.push(VerifierHandle { - deleting: deleting.clone(), - thread: thread::Builder::new() - .name(format!("Verifier #{}", len)) - .spawn(move || { - panic_handler.catch_panic(move || { - VerificationQueue::verify(verification, engine, wait, ready, deleting, empty) - }).unwrap() - }) - .expect("Failed to create verifier thread.") - }); - } - - // remove a verifier thread if possible. - fn remove_verifier(&self) { - let mut verifiers = self.verifiers.lock(); - let len = verifiers.len(); - - // never remove the last thread. - if len == 1 { - return; + // scaling down. + for i in target..*verifier_count { + debug!(target: "verification", "Putting verifier {} to sleep", i); + verifiers[i].sleep(); } - debug!(target: "verification", "Removing verification thread #{}", len - 1); - - if let Some(handle) = verifiers.pop() { - handle.conclude(); - self.more_to_verify.notify_all(); // to ensure it's joinable immediately. - handle.join(); - } + *verifier_count = target; } } @@ -631,7 +670,8 @@ impl Drop for VerificationQueue { self.clear(); self.deleting.store(true, AtomicOrdering::Release); - let mut verifiers = self.verifiers.lock(); + let mut verifiers = self.verifiers.get_mut(); + let mut verifiers = &mut verifiers.0; // first pass to signal conclusion. must be done before // notify or deadlock possible. From 8c0e511ebe0a5541fa804ad2a8393f12afec1d6e Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 17 Nov 2016 18:10:09 +0100 Subject: [PATCH 09/97] rewrite scaling logic --- ethcore/src/verification/queue/mod.rs | 72 ++++++++++++--------------- 1 file changed, 31 insertions(+), 41 deletions(-) diff --git a/ethcore/src/verification/queue/mod.rs b/ethcore/src/verification/queue/mod.rs index f4a9287d6..b4f2ab5a2 100644 --- a/ethcore/src/verification/queue/mod.rs +++ b/ethcore/src/verification/queue/mod.rs @@ -191,9 +191,6 @@ struct Verification { bad: Mutex>, more_to_verify: SMutex<()>, empty: SMutex<()>, - verified_count: AtomicUsize, - drained: AtomicUsize, - imported: AtomicUsize, sizes: Sizes, check_seal: bool, } @@ -208,9 +205,6 @@ impl VerificationQueue { bad: Mutex::new(HashSet::new()), more_to_verify: SMutex::new(()), empty: SMutex::new(()), - verified_count: AtomicUsize::new(0), - drained: AtomicUsize::new(0), - imported: AtomicUsize::new(0), sizes: Sizes { unverified: AtomicUsize::new(0), verifying: AtomicUsize::new(0), @@ -355,7 +349,7 @@ impl VerificationQueue { // we're next! let mut verified = verification.verified.lock(); let mut bad = verification.bad.lock(); - VerificationQueue::drain_verifying(&mut verifying, &mut verified, &mut bad, &verification.verified_count, &verification.sizes); + VerificationQueue::drain_verifying(&mut verifying, &mut verified, &mut bad, &verification.sizes); true } else { false @@ -370,7 +364,7 @@ impl VerificationQueue { verifying.retain(|e| e.hash != hash); if verifying.front().map_or(false, |x| x.output.is_some()) { - VerificationQueue::drain_verifying(&mut verifying, &mut verified, &mut bad, &verification.verified_count, &verification.sizes); + VerificationQueue::drain_verifying(&mut verifying, &mut verified, &mut bad, &verification.sizes); true } else { false @@ -388,10 +382,8 @@ impl VerificationQueue { verifying: &mut VecDeque>, verified: &mut VecDeque, bad: &mut HashSet, - v_count: &AtomicUsize, sizes: &Sizes, ) { - let start_len = verified.len(); let mut removed_size = 0; let mut inserted_size = 0; @@ -408,7 +400,6 @@ impl VerificationQueue { } } - v_count.fetch_add(verified.len() - start_len, AtomicOrdering::AcqRel); sizes.verifying.fetch_sub(removed_size, AtomicOrdering::SeqCst); sizes.verified.fetch_add(inserted_size, AtomicOrdering::SeqCst); } @@ -474,7 +465,6 @@ impl VerificationQueue { self.processing.write().insert(h.clone()); self.verification.unverified.lock().push_back(item); - self.verification.imported.fetch_add(1, AtomicOrdering::AcqRel); self.more_to_verify.notify_all(); Ok(h) }, @@ -536,8 +526,6 @@ impl VerificationQueue { let count = min(max, verified.len()); let result = verified.drain(..count).collect::>(); - self.verification.drained.fetch_add(result.len(), AtomicOrdering::AcqRel); - let drained_size = result.iter().map(HeapSizeOf::heap_size_of_children).fold(0, |a, c| a + c); self.verification.sizes.verified.fetch_sub(drained_size, AtomicOrdering::SeqCst); @@ -588,11 +576,22 @@ impl VerificationQueue { // when deciding whether to change the number of verifiers. const READJUSTMENT_PERIOD: usize = 12; - { - self.verification.unverified.lock().shrink_to_fit(); + let (u_len, v_len) = { + let u_len = { + let mut q = self.verification.unverified.lock(); + q.shrink_to_fit(); + q.len() + }; self.verification.verifying.lock().shrink_to_fit(); - self.verification.verified.lock().shrink_to_fit(); - } + + let v_len = { + let mut q = self.verification.verified.lock(); + q.shrink_to_fit(); + q.len() + }; + + (u_len as isize, v_len as isize) + }; self.processing.write().shrink_to_fit(); @@ -603,31 +602,22 @@ impl VerificationQueue { return; } - let v_count = self.verification.verified_count.load(AtomicOrdering::Acquire); - let drained = self.verification.drained.load(AtomicOrdering::Acquire); - let imported = self.verification.imported.load(AtomicOrdering::Acquire); + let current = self.verifiers.lock().1; - self.verification.verified_count.store(0, AtomicOrdering::Release); - self.verification.drained.store(0, AtomicOrdering::Release); - self.verification.imported.store(0, AtomicOrdering::Release); + let diff = (v_len - u_len).abs(); + let total = v_len + u_len; - // select which side of the queue is the bottleneck. - let target = min(drained, imported); - - // compute the average rate of verification per thread and determine - // how many are necessary to match the rate of draining. - let num_verifiers = self.verifiers.lock().1; - let v_count_per = v_count as f64 / num_verifiers as f64; - let needed = if v_count < 20 { - 1 - } else { - (target as f64 / v_count_per as f64).ceil() as usize - }; - - trace!(target: "verification", "v_rate_per={}, target={}, scaling to {} verifiers", - v_count_per, target, needed); - - self.scale_verifiers(needed); + self.scale_verifiers( + if u_len < 20 { + 1 + } else if diff <= total / 10 { + current + } else if v_len > u_len { + current - 1 + } else { + current + 1 + } + ); } // wake up or sleep verifiers to get as close to the target as From e69b7f66a3a530812a01e6ba9b05190c4066d4e6 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 18 Nov 2016 14:18:50 +0100 Subject: [PATCH 10/97] Gas calculation & limit errors --- .../DetailsStep/detailsStep.js | 16 +++++ .../ExecuteContract/executeContract.css | 8 +++ .../modals/ExecuteContract/executeContract.js | 72 +++++++++++++++++-- js/src/modals/Transfer/errors.js | 4 +- js/src/modals/Transfer/transfer.css | 9 +++ js/src/modals/Transfer/transfer.js | 57 +++++++++++++-- js/src/util/constants.js | 26 +++++++ 7 files changed, 180 insertions(+), 12 deletions(-) create mode 100644 js/src/util/constants.js diff --git a/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js b/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js index b549abb18..aad54e360 100644 --- a/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js +++ b/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js @@ -36,6 +36,7 @@ export default class DetailsStep extends Component { onFuncChange: PropTypes.func, values: PropTypes.array.isRequired, valuesError: PropTypes.array.isRequired, + warning: PropTypes.string, onValueChange: PropTypes.func.isRequired } @@ -44,6 +45,7 @@ export default class DetailsStep extends Component { return (
+ { this.renderWarning() } + { warning } + + ); + } + onFuncChange = (event, index, signature) => { const { contract, onFuncChange } = this.props; diff --git a/js/src/modals/ExecuteContract/executeContract.css b/js/src/modals/ExecuteContract/executeContract.css index cdebfef32..55135a92e 100644 --- a/js/src/modals/ExecuteContract/executeContract.css +++ b/js/src/modals/ExecuteContract/executeContract.css @@ -33,3 +33,11 @@ .txhash { word-break: break-all; } + +.warning { + border-radius: 0.5em; + background: #f80; + color: white; + padding: 0.75em; + text-align: center; +} diff --git a/js/src/modals/ExecuteContract/executeContract.js b/js/src/modals/ExecuteContract/executeContract.js index a57c18a1d..8022690f5 100644 --- a/js/src/modals/ExecuteContract/executeContract.js +++ b/js/src/modals/ExecuteContract/executeContract.js @@ -15,17 +15,21 @@ // along with Parity. If not, see . import React, { Component, PropTypes } from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; import ActionDoneAll from 'material-ui/svg-icons/action/done-all'; import ContentClear from 'material-ui/svg-icons/content/clear'; import { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash } from '../../ui'; +import { MAX_GAS_ESTIMATION } from '../../util/constants'; import { validateAddress, validateUint } from '../../util/validation'; import DetailsStep from './DetailsStep'; +import ERRORS from '../Transfer/errors'; import { ERROR_CODES } from '../../api/transport/error'; -export default class ExecuteContract extends Component { +class ExecuteContract extends Component { static contextTypes = { api: PropTypes.object.isRequired, store: PropTypes.object.isRequired @@ -36,6 +40,7 @@ export default class ExecuteContract extends Component { fromAddress: PropTypes.string, accounts: PropTypes.object, contract: PropTypes.object, + gasLimit: PropTypes.object.isRequired, onClose: PropTypes.func.isRequired, onFromAddressChange: PropTypes.func.isRequired } @@ -46,6 +51,8 @@ export default class ExecuteContract extends Component { fromAddressError: null, func: null, funcError: null, + gas: null, + gasLimitError: null, values: [], valuesError: [], step: 0, @@ -119,7 +126,7 @@ export default class ExecuteContract extends Component { renderStep () { const { onFromAddressChange } = this.props; - const { step, busyState, txhash, rejected } = this.state; + const { step, busyState, gasLimitError, txhash, rejected } = this.state; if (rejected) { return ( @@ -135,6 +142,7 @@ export default class ExecuteContract extends Component { { + this.estimateGas(); this.setState({ amount }); } @@ -179,6 +188,7 @@ export default class ExecuteContract extends Component { } }); + this.estimateGas(); this.setState({ func, values @@ -208,17 +218,54 @@ export default class ExecuteContract extends Component { values[index] = value; valuesError[index] = valueError; + if (!valueError) { + this.estimateGas(); + } + this.setState({ values: [].concat(values), valuesError: [].concat(valuesError) }); } + estimateGas = () => { + const { api } = this.context; + const { fromAddress, gasLimit } = this.props; + const { amount, func, values } = this.state; + const options = { + gas: MAX_GAS_ESTIMATION, + from: fromAddress, + value: api.util.toWei(amount || 0) + }; + + func + .estimateGas(options, values) + .then((gasEst) => { + const gas = gasEst.mul(1.2); + let gasLimitError = null; + + if (gas.gte(MAX_GAS_ESTIMATION)) { + gasLimitError = ERRORS.gasException; + } else if (gas.gt(gasLimit)) { + gasLimitError = ERRORS.gasBlockLimit; + } + + this.setState({ + gas, + gasLimitError + }); + }) + .catch((error) => { + console.warn('estimateGas', error); + }); + } + postTransaction = () => { const { api, store } = this.context; const { fromAddress } = this.props; const { amount, func, values } = this.state; const options = { + gas: MAX_GAS_ESTIMATION, from: fromAddress, value: api.util.toWei(amount || 0) }; @@ -237,13 +284,13 @@ export default class ExecuteContract extends Component { return api .pollMethod('parity_checkRequest', requestId) - .catch((e) => { - if (e.code === ERROR_CODES.REQUEST_REJECTED) { + .catch((error) => { + if (error.code === ERROR_CODES.REQUEST_REJECTED) { this.setState({ rejected: true }); return false; } - throw e; + throw error; }); }) .then((txhash) => { @@ -255,3 +302,18 @@ export default class ExecuteContract extends Component { }); } } + +function mapStateToProps (state) { + const { gasLimit } = state.status; + + return { gasLimit }; +} + +function mapDispatchToProps (dispatch) { + return bindActionCreators({}, dispatch); +} + +export default connect( + mapStateToProps, + mapDispatchToProps +)(ExecuteContract); diff --git a/js/src/modals/Transfer/errors.js b/js/src/modals/Transfer/errors.js index a6456c785..c35d82636 100644 --- a/js/src/modals/Transfer/errors.js +++ b/js/src/modals/Transfer/errors.js @@ -19,7 +19,9 @@ const ERRORS = { invalidAddress: 'the supplied address is an invalid network address', invalidAmount: 'the supplied amount should be a valid positive number', invalidDecimals: 'the supplied amount exceeds the allowed decimals', - largeAmount: 'the transaction total is higher than the available balance' + largeAmount: 'the transaction total is higher than the available balance', + gasException: 'the transaction indicates a thrown exception', + gasBlockLimit: 'the transaction will exceed the block gas limit' }; export default ERRORS; diff --git a/js/src/modals/Transfer/transfer.css b/js/src/modals/Transfer/transfer.css index 89b58666e..76f83c62b 100644 --- a/js/src/modals/Transfer/transfer.css +++ b/js/src/modals/Transfer/transfer.css @@ -14,6 +14,7 @@ /* You should have received a copy of the GNU General Public License /* along with Parity. If not, see . */ + .info { line-height: 1.618em; width: 100%; @@ -151,3 +152,11 @@ .gasPriceDesc { font-size: 0.9em; } + +.warning { + border-radius: 0.5em; + background: #f80; + color: white; + padding: 0.75em; + text-align: center; +} diff --git a/js/src/modals/Transfer/transfer.js b/js/src/modals/Transfer/transfer.js index e41ea08f9..196307923 100644 --- a/js/src/modals/Transfer/transfer.js +++ b/js/src/modals/Transfer/transfer.js @@ -16,12 +16,15 @@ import BigNumber from 'bignumber.js'; import React, { Component, PropTypes } from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; import ActionDoneAll from 'material-ui/svg-icons/action/done-all'; import ContentClear from 'material-ui/svg-icons/content/clear'; import NavigationArrowBack from 'material-ui/svg-icons/navigation/arrow-back'; import NavigationArrowForward from 'material-ui/svg-icons/navigation/arrow-forward'; import { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash } from '../../ui'; +import { DEFAULT_GAS, DEFAULT_GASPRICE, MAX_GAS_ESTIMATION } from '../../util/constants'; import Details from './Details'; import Extras from './Extras'; @@ -30,8 +33,6 @@ import styles from './transfer.css'; import { ERROR_CODES } from '../../api/transport/error'; -const DEFAULT_GAS = '21000'; -const DEFAULT_GASPRICE = '20000000000'; const TITLES = { transfer: 'transfer details', sending: 'sending', @@ -42,7 +43,7 @@ const TITLES = { const STAGES_BASIC = [TITLES.transfer, TITLES.sending, TITLES.complete]; const STAGES_EXTRA = [TITLES.transfer, TITLES.extras, TITLES.sending, TITLES.complete]; -export default class Transfer extends Component { +class Transfer extends Component { static contextTypes = { api: PropTypes.object.isRequired, store: PropTypes.object.isRequired @@ -52,6 +53,7 @@ export default class Transfer extends Component { account: PropTypes.object, balance: PropTypes.object, balances: PropTypes.object, + gasLimit: PropTypes.object.isRequired, images: PropTypes.object.isRequired, onClose: PropTypes.func } @@ -64,6 +66,7 @@ export default class Transfer extends Component { gas: DEFAULT_GAS, gasEst: '0', gasError: null, + gasLimitError: null, gasPrice: DEFAULT_GASPRICE, gasPriceHistogram: {}, gasPriceError: null, @@ -103,6 +106,7 @@ export default class Transfer extends Component { visible scroll > + { this.renderWarning() } { this.renderPage() } ); @@ -264,6 +268,20 @@ export default class Transfer extends Component { } } + renderWarning () { + const { gasLimitError } = this.state; + + if (!gasLimitError) { + return null; + } + + return ( +
+ { gasLimitError } +
+ ); + } + isValid () { const detailsValid = !this.state.recipientError && !this.state.valueError && !this.state.totalError; const extrasValid = !this.state.gasError && !this.state.gasPriceError && !this.state.totalError; @@ -519,6 +537,7 @@ export default class Transfer extends Component { return token.contract.instance.transfer .estimateGas({ + gas: MAX_GAS_ESTIMATION, from: account.address, to: token.address }, [ @@ -532,6 +551,7 @@ export default class Transfer extends Component { const { account } = this.props; const { data, recipient, value } = this.state; const options = { + gas: MAX_GAS_ESTIMATION, from: account.address, to: recipient, value: api.util.toWei(value || 0) @@ -552,19 +572,29 @@ export default class Transfer extends Component { return; } + const { gasLimit } = this.props; + (this.state.isEth ? this._estimateGasEth() : this._estimateGasToken() - ).then((_value) => { - let gas = _value; + ).then((gasEst) => { + let gas = gasEst; + let gasLimitError = null; if (gas.gt(DEFAULT_GAS)) { gas = gas.mul(1.2); } + if (gas.gte(MAX_GAS_ESTIMATION)) { + gasLimitError = ERRORS.gasException; + } else if (gas.gt(gasLimit)) { + gasLimitError = ERRORS.gasBlockLimit; + } + this.setState({ gas: gas.toFixed(0), - gasEst: _value.toFormat() + gasEst: gasEst.toFormat(), + gasLimitError }, this.recalculate); }) .catch((error) => { @@ -649,3 +679,18 @@ export default class Transfer extends Component { store.dispatch({ type: 'newError', error }); } } + +function mapStateToProps (state) { + const { gasLimit } = state.status; + + return { gasLimit }; +} + +function mapDispatchToProps (dispatch) { + return bindActionCreators({}, dispatch); +} + +export default connect( + mapStateToProps, + mapDispatchToProps +)(Transfer); diff --git a/js/src/util/constants.js b/js/src/util/constants.js new file mode 100644 index 000000000..87ca59668 --- /dev/null +++ b/js/src/util/constants.js @@ -0,0 +1,26 @@ +// 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 . + +const DEFAULT_GAS = '21000'; +const DEFAULT_GASPRICE = '20000000000'; + +const MAX_GAS_ESTIMATION = '50000000'; + +export default { + DEFAULT_GAS, + DEFAULT_GASPRICE, + MAX_GAS_ESTIMATION +}; From b433e7e9a0207b6d3c6b7403ea9c54dee6787fc9 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 18 Nov 2016 15:05:02 +0100 Subject: [PATCH 11/97] Updated, contract execution in-place --- .../ExecuteContract/executeContract.css | 1 + .../modals/ExecuteContract/executeContract.js | 31 ++++++++++++------- js/src/modals/Transfer/errors.js | 4 +-- js/src/modals/Transfer/transfer.css | 1 + js/src/modals/Transfer/transfer.js | 2 +- js/src/util/constants.js | 2 +- 6 files changed, 26 insertions(+), 15 deletions(-) diff --git a/js/src/modals/ExecuteContract/executeContract.css b/js/src/modals/ExecuteContract/executeContract.css index 55135a92e..a83b373ee 100644 --- a/js/src/modals/ExecuteContract/executeContract.css +++ b/js/src/modals/ExecuteContract/executeContract.css @@ -38,6 +38,7 @@ border-radius: 0.5em; background: #f80; color: white; + font-size: 0.75em; padding: 0.75em; text-align: center; } diff --git a/js/src/modals/ExecuteContract/executeContract.js b/js/src/modals/ExecuteContract/executeContract.js index 8022690f5..1354e8543 100644 --- a/js/src/modals/ExecuteContract/executeContract.js +++ b/js/src/modals/ExecuteContract/executeContract.js @@ -71,6 +71,12 @@ class ExecuteContract extends Component { this.onFuncChange(null, functions[0]); } + componentWillReceiveProps (newProps) { + if (newProps.fromAddress !== this.props.fromAddress) { + this.estimateGas(newProps.fromAddress); + } + } + render () { const { sending } = this.state; @@ -164,8 +170,7 @@ class ExecuteContract extends Component { } onAmountChange = (amount) => { - this.estimateGas(); - this.setState({ amount }); + this.setState({ amount }, this.estimateGas); } onFuncChange = (event, func) => { @@ -188,11 +193,10 @@ class ExecuteContract extends Component { } }); - this.estimateGas(); this.setState({ func, values - }); + }, this.estimateGas); } onValueChange = (event, index, _value) => { @@ -218,29 +222,34 @@ class ExecuteContract extends Component { values[index] = value; valuesError[index] = valueError; - if (!valueError) { - this.estimateGas(); - } - this.setState({ values: [].concat(values), valuesError: [].concat(valuesError) + }, () => { + if (!valueError) { + this.estimateGas(); + } }); } - estimateGas = () => { + estimateGas = (_fromAddress) => { const { api } = this.context; const { fromAddress, gasLimit } = this.props; const { amount, func, values } = this.state; const options = { gas: MAX_GAS_ESTIMATION, - from: fromAddress, + from: _fromAddress || fromAddress, value: api.util.toWei(amount || 0) }; + if (!func) { + return; + } + func .estimateGas(options, values) .then((gasEst) => { + console.log(gasEst.toFormat(0)); const gas = gasEst.mul(1.2); let gasLimitError = null; @@ -304,7 +313,7 @@ class ExecuteContract extends Component { } function mapStateToProps (state) { - const { gasLimit } = state.status; + const { gasLimit } = state.nodeStatus; return { gasLimit }; } diff --git a/js/src/modals/Transfer/errors.js b/js/src/modals/Transfer/errors.js index c35d82636..3a6bd63ae 100644 --- a/js/src/modals/Transfer/errors.js +++ b/js/src/modals/Transfer/errors.js @@ -20,8 +20,8 @@ const ERRORS = { invalidAmount: 'the supplied amount should be a valid positive number', invalidDecimals: 'the supplied amount exceeds the allowed decimals', largeAmount: 'the transaction total is higher than the available balance', - gasException: 'the transaction indicates a thrown exception', - gasBlockLimit: 'the transaction will exceed the block gas limit' + gasException: 'the transaction will throw an exception with the current values', + gasBlockLimit: 'the transaction execution will exceed the block gas limit' }; export default ERRORS; diff --git a/js/src/modals/Transfer/transfer.css b/js/src/modals/Transfer/transfer.css index 76f83c62b..b7f478b4f 100644 --- a/js/src/modals/Transfer/transfer.css +++ b/js/src/modals/Transfer/transfer.css @@ -157,6 +157,7 @@ border-radius: 0.5em; background: #f80; color: white; + font-size: 0.75em; padding: 0.75em; text-align: center; } diff --git a/js/src/modals/Transfer/transfer.js b/js/src/modals/Transfer/transfer.js index 196307923..e906e45d7 100644 --- a/js/src/modals/Transfer/transfer.js +++ b/js/src/modals/Transfer/transfer.js @@ -681,7 +681,7 @@ class Transfer extends Component { } function mapStateToProps (state) { - const { gasLimit } = state.status; + const { gasLimit } = state.nodeStatus; return { gasLimit }; } diff --git a/js/src/util/constants.js b/js/src/util/constants.js index 87ca59668..d0664d25e 100644 --- a/js/src/util/constants.js +++ b/js/src/util/constants.js @@ -19,7 +19,7 @@ const DEFAULT_GASPRICE = '20000000000'; const MAX_GAS_ESTIMATION = '50000000'; -export default { +export { DEFAULT_GAS, DEFAULT_GASPRICE, MAX_GAS_ESTIMATION From 8d339ad379239f1968693952f39c3340dbbd8bf2 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 18 Nov 2016 15:13:37 +0100 Subject: [PATCH 12/97] Adjust styling --- js/src/modals/Transfer/transfer.css | 1 + 1 file changed, 1 insertion(+) diff --git a/js/src/modals/Transfer/transfer.css b/js/src/modals/Transfer/transfer.css index b7f478b4f..3bd17ba96 100644 --- a/js/src/modals/Transfer/transfer.css +++ b/js/src/modals/Transfer/transfer.css @@ -158,6 +158,7 @@ background: #f80; color: white; font-size: 0.75em; + margin-bottom: 1em; padding: 0.75em; text-align: center; } From f827ade6161caca36ec7cecffb7e1eba2b0da762 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 18 Nov 2016 16:32:57 +0100 Subject: [PATCH 13/97] debug log removal --- js/src/modals/ExecuteContract/executeContract.js | 1 - 1 file changed, 1 deletion(-) diff --git a/js/src/modals/ExecuteContract/executeContract.js b/js/src/modals/ExecuteContract/executeContract.js index 1354e8543..97d44a029 100644 --- a/js/src/modals/ExecuteContract/executeContract.js +++ b/js/src/modals/ExecuteContract/executeContract.js @@ -249,7 +249,6 @@ class ExecuteContract extends Component { func .estimateGas(options, values) .then((gasEst) => { - console.log(gasEst.toFormat(0)); const gas = gasEst.mul(1.2); let gasLimitError = null; From 1686d23522f8d1ddd8e984982402a67ba4ef733b Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 18 Nov 2016 19:07:13 +0100 Subject: [PATCH 14/97] Don't query chain in Signer, use Redux isTest --- .../Signer/components/Account/Account.js | 18 ++++++---- .../Account/AccountLink/AccountLink.js | 14 ++++---- .../RequestFinishedWeb3.js | 9 +++-- .../RequestPendingWeb3/RequestPendingWeb3.js | 9 +++-- .../components/SignRequest/SignRequest.js | 33 +++++++++---------- .../TransactionFinished.js | 27 +++++++-------- .../TransactionMainDetails.js | 30 +++++++++++++---- .../TransactionPending/TransactionPending.js | 21 ++---------- .../TransactionSecondaryDetails.js | 10 ++++-- .../components/TxHashLink/TxHashLink.js | 6 ++-- .../Signer/containers/Embedded/embedded.js | 10 ++++-- .../containers/RequestsPage/RequestsPage.js | 17 ++++++++-- 12 files changed, 117 insertions(+), 87 deletions(-) diff --git a/js/src/views/Signer/components/Account/Account.js b/js/src/views/Signer/components/Account/Account.js index 63a601373..b6e7f2e48 100644 --- a/js/src/views/Signer/components/Account/Account.js +++ b/js/src/views/Signer/components/Account/Account.js @@ -25,7 +25,7 @@ export default class Account extends Component { static propTypes = { className: PropTypes.string, address: PropTypes.string.isRequired, - chain: PropTypes.string.isRequired, + isTest: PropTypes.bool.isRequired, balance: PropTypes.object // eth BigNumber, not required since it mght take time to fetch }; @@ -51,11 +51,13 @@ export default class Account extends Component { } render () { - const { address, chain, className } = this.props; + const { address, isTest, className } = this.props; return (
- + @@ -74,19 +76,23 @@ export default class Account extends Component { } renderName () { - const { address } = this.props; + const { address, isTest } = this.props; const name = ; if (!name) { return ( - + [{ this.shortAddress(address) }] ); } return ( - + { name } [{ this.tinyAddress(address) }] diff --git a/js/src/views/Signer/components/Account/AccountLink/AccountLink.js b/js/src/views/Signer/components/Account/AccountLink/AccountLink.js index 4e3c0a0a9..97ff35ce9 100644 --- a/js/src/views/Signer/components/Account/AccountLink/AccountLink.js +++ b/js/src/views/Signer/components/Account/AccountLink/AccountLink.js @@ -21,7 +21,7 @@ import styles from './AccountLink.css'; export default class AccountLink extends Component { static propTypes = { - chain: PropTypes.string.isRequired, + isTest: PropTypes.bool.isRequired, address: PropTypes.string.isRequired, className: PropTypes.string, children: PropTypes.node @@ -32,15 +32,15 @@ export default class AccountLink extends Component { }; componentWillMount () { - const { address, chain } = this.props; + const { address, isTest } = this.props; - this.updateLink(address, chain); + this.updateLink(address, isTest); } componentWillReceiveProps (nextProps) { - const { address, chain } = nextProps; + const { address, isTest } = nextProps; - this.updateLink(address, chain); + this.updateLink(address, isTest); } render () { @@ -56,8 +56,8 @@ export default class AccountLink extends Component { ); } - updateLink (address, chain) { - const link = addressLink(address, chain === 'morden' || chain === 'testnet'); + updateLink (address, isTest) { + const link = addressLink(address, isTest); this.setState({ link diff --git a/js/src/views/Signer/components/RequestFinishedWeb3/RequestFinishedWeb3.js b/js/src/views/Signer/components/RequestFinishedWeb3/RequestFinishedWeb3.js index 7b30d3e93..74a20f625 100644 --- a/js/src/views/Signer/components/RequestFinishedWeb3/RequestFinishedWeb3.js +++ b/js/src/views/Signer/components/RequestFinishedWeb3/RequestFinishedWeb3.js @@ -31,14 +31,16 @@ export default class RequestFinishedWeb3 extends Component { msg: PropTypes.string, status: PropTypes.string, error: PropTypes.string, - className: PropTypes.string + className: PropTypes.string, + isTest: PropTypes.bool.isRequired } render () { - const { payload, id, result, msg, status, error, date, className } = this.props; + const { payload, id, result, msg, status, error, date, className, isTest } = this.props; if (payload.sign) { const { sign } = payload; + return ( ); } if (payload.transaction) { const { transaction } = payload; + return ( ); } diff --git a/js/src/views/Signer/components/RequestPendingWeb3/RequestPendingWeb3.js b/js/src/views/Signer/components/RequestPendingWeb3/RequestPendingWeb3.js index 97fa43f69..d9e695dbc 100644 --- a/js/src/views/Signer/components/RequestPendingWeb3/RequestPendingWeb3.js +++ b/js/src/views/Signer/components/RequestPendingWeb3/RequestPendingWeb3.js @@ -30,7 +30,8 @@ export default class RequestPendingWeb3 extends Component { PropTypes.shape({ transaction: PropTypes.object.isRequired }), PropTypes.shape({ sign: PropTypes.object.isRequired }) ]).isRequired, - className: PropTypes.string + className: PropTypes.string, + isTest: PropTypes.bool.isRequired }; onConfirm = data => { @@ -41,10 +42,11 @@ export default class RequestPendingWeb3 extends Component { }; render () { - const { payload, id, className, isSending, date, onReject } = this.props; + const { payload, id, className, isSending, date, onReject, isTest } = this.props; if (payload.sign) { const { sign } = payload; + return ( ); } if (payload.transaction) { const { transaction } = payload; + return ( ); } diff --git a/js/src/views/Signer/components/SignRequest/SignRequest.js b/js/src/views/Signer/components/SignRequest/SignRequest.js index 719ddeec9..6fb2b37b7 100644 --- a/js/src/views/Signer/components/SignRequest/SignRequest.js +++ b/js/src/views/Signer/components/SignRequest/SignRequest.js @@ -39,24 +39,15 @@ export default class SignRequest extends Component { onReject: PropTypes.func, status: PropTypes.string, className: PropTypes.string, - chain: nullable(PropTypes.object), + isTest: PropTypes.bool.isRequired, balance: nullable(PropTypes.object) }; state = { - chain: null, balance: null } componentWillMount () { - this.context.api.parity.netChain() - .then((chain) => { - this.setState({ chain }); - }) - .catch((err) => { - console.error('could not fetch chain', err); - }); - this.context.api.eth.getBalance(this.props.address) .then((balance) => { this.setState({ balance }); @@ -68,6 +59,7 @@ export default class SignRequest extends Component { render () { const { className } = this.props; + return (
{ this.renderDetails() } @@ -77,15 +69,20 @@ export default class SignRequest extends Component { } renderDetails () { - const { address, hash } = this.props; - const { balance, chain } = this.state; + const { address, hash, isTest } = this.props; + const { balance } = this.state; - if (!balance || !chain) return (
); + if (!balance) { + return
; + } return (
- +

Dapp is requesting to sign arbitrary transaction using this account.

@@ -100,15 +97,17 @@ export default class SignRequest extends Component { if (isFinished) { if (status === 'confirmed') { - const { hash } = this.props; - const { chain } = this.state; + const { hash, isTest } = this.props; return (
Confirmed
Transaction hash: - +
); diff --git a/js/src/views/Signer/components/TransactionFinished/TransactionFinished.js b/js/src/views/Signer/components/TransactionFinished/TransactionFinished.js index 00d6a057f..54333115d 100644 --- a/js/src/views/Signer/components/TransactionFinished/TransactionFinished.js +++ b/js/src/views/Signer/components/TransactionFinished/TransactionFinished.js @@ -47,13 +47,12 @@ export default class TransactionFinished extends Component { txHash: PropTypes.string, // undefined if transacation is rejected className: PropTypes.string, data: PropTypes.string, - chain: nullable(PropTypes.object), + isTest: PropTypes.bool.isRequired, fromBalance: nullable(PropTypes.object), toBalance: nullable(PropTypes.object) }; state = { - chain: null, fromBalance: null, toBalance: null }; @@ -64,14 +63,6 @@ export default class TransactionFinished extends Component { const totalValue = tUtil.getTotalValue(fee, value); this.setState({ totalValue }); - this.context.api.parity.netChain() - .then((chain) => { - this.setState({ chain }); - }) - .catch((err) => { - console.error('could not fetch chain', err); - }); - this.fetchBalance(from, 'fromBalance'); if (to) { this.fetchBalance(to, 'toBalance'); @@ -79,8 +70,9 @@ export default class TransactionFinished extends Component { } render () { - const { chain, fromBalance, toBalance } = this.state; - if (!chain || !fromBalance || !toBalance) { + const { fromBalance, toBalance } = this.state; + + if (!fromBalance || !toBalance) { return (
@@ -130,16 +122,19 @@ export default class TransactionFinished extends Component { } renderTxHash () { - const { txHash } = this.props; - const { chain } = this.state; - if (!txHash || !chain) { + const { txHash, isTest } = this.props; + + if (!txHash) { return; } return (
Transaction hash: - +
); } diff --git a/js/src/views/Signer/components/TransactionMainDetails/TransactionMainDetails.js b/js/src/views/Signer/components/TransactionMainDetails/TransactionMainDetails.js index 10b54b10b..4efa567d2 100644 --- a/js/src/views/Signer/components/TransactionMainDetails/TransactionMainDetails.js +++ b/js/src/views/Signer/components/TransactionMainDetails/TransactionMainDetails.js @@ -30,7 +30,7 @@ export default class TransactionMainDetails extends Component { fromBalance: PropTypes.object, // eth BigNumber, not required since it might take time to fetch value: PropTypes.object.isRequired, // wei hex totalValue: PropTypes.object.isRequired, // wei BigNumber - chain: PropTypes.string.isRequired, + isTest: PropTypes.isTest.isRequired, to: PropTypes.string, // undefined if it's a contract toBalance: PropTypes.object, // eth BigNumber - undefined if it's a contract or until it's fetched className: PropTypes.string, @@ -39,11 +39,13 @@ export default class TransactionMainDetails extends Component { componentWillMount () { const { value, totalValue } = this.props; + this.updateDisplayValues(value, totalValue); } componentWillReceiveProps (nextProps) { const { value, totalValue } = nextProps; + this.updateDisplayValues(value, totalValue); } @@ -59,6 +61,7 @@ export default class TransactionMainDetails extends Component { render () { const { className, children } = this.props; + return (
{ this.renderTransfer() } @@ -69,7 +72,8 @@ export default class TransactionMainDetails extends Component { } renderTransfer () { - const { from, fromBalance, to, toBalance, chain } = this.props; + const { from, fromBalance, to, toBalance, isTest } = this.props; + if (!to) { return; } @@ -78,7 +82,10 @@ export default class TransactionMainDetails extends Component {
- +
@@ -88,7 +95,10 @@ export default class TransactionMainDetails extends Component {
- +
@@ -96,15 +106,20 @@ export default class TransactionMainDetails extends Component { } renderContract () { - const { from, fromBalance, to, chain } = this.props; + const { from, fromBalance, to, isTest } = this.props; + if (to) { return; } + return (
- +
@@ -126,6 +141,7 @@ export default class TransactionMainDetails extends Component { renderValue () { const { id } = this.props; const { valueDisplay, valueDisplayWei } = this.state; + return (
); } - } diff --git a/js/src/views/Signer/components/TransactionPending/TransactionPending.js b/js/src/views/Signer/components/TransactionPending/TransactionPending.js index 55e4f6405..55f09ac97 100644 --- a/js/src/views/Signer/components/TransactionPending/TransactionPending.js +++ b/js/src/views/Signer/components/TransactionPending/TransactionPending.js @@ -16,7 +16,6 @@ import React, { Component, PropTypes } from 'react'; -import CircularProgress from 'material-ui/CircularProgress'; import TransactionMainDetails from '../TransactionMainDetails'; import TransactionPendingForm from '../TransactionPendingForm'; import TransactionSecondaryDetails from '../TransactionSecondaryDetails'; @@ -43,7 +42,8 @@ export default class TransactionPending extends Component { onConfirm: PropTypes.func.isRequired, onReject: PropTypes.func.isRequired, isSending: PropTypes.bool.isRequired, - className: PropTypes.string + className: PropTypes.string, + isTest: PropTypes.bool.isRequired }; static defaultProps = { @@ -51,7 +51,6 @@ export default class TransactionPending extends Component { }; state = { - chain: null, fromBalance: null, toBalance: null }; @@ -64,28 +63,12 @@ export default class TransactionPending extends Component { const gasToDisplay = tUtil.getGasDisplay(gas); this.setState({ gasPriceEthmDisplay, totalValue, gasToDisplay }); - this.context.api.parity.netChain() - .then((chain) => { - this.setState({ chain }); - }) - .catch((err) => { - console.error('could not fetch chain', err); - }); - const { from, to } = this.props; this.fetchBalance(from, 'fromBalance'); if (to) this.fetchBalance(to, 'toBalance'); } render () { - if (!this.state.chain) { - return ( -
- -
- ); - } - const { totalValue, gasPriceEthmDisplay, gasToDisplay } = this.state; const { className, id, date, data, from } = this.props; diff --git a/js/src/views/Signer/components/TransactionSecondaryDetails/TransactionSecondaryDetails.js b/js/src/views/Signer/components/TransactionSecondaryDetails/TransactionSecondaryDetails.js index 61c1260d4..fb0a329e0 100644 --- a/js/src/views/Signer/components/TransactionSecondaryDetails/TransactionSecondaryDetails.js +++ b/js/src/views/Signer/components/TransactionSecondaryDetails/TransactionSecondaryDetails.js @@ -59,10 +59,13 @@ export default class TransactionSecondaryDetails extends Component { } renderGasPrice () { - if (!this.props.gasPriceEthmDisplay && !this.props.gasToDisplay) return null; + if (!this.props.gasPriceEthmDisplay && !this.props.gasToDisplay) { + return null; + } const { id } = this.props; const { gasPriceEthmDisplay, gasToDisplay } = this.props; + return (
{ children || txHash } diff --git a/js/src/views/Signer/containers/Embedded/embedded.js b/js/src/views/Signer/containers/Embedded/embedded.js index be3a65d61..e06eaf274 100644 --- a/js/src/views/Signer/containers/Embedded/embedded.js +++ b/js/src/views/Signer/containers/Embedded/embedded.js @@ -35,7 +35,8 @@ class Embedded extends Component { actions: PropTypes.shape({ startConfirmRequest: PropTypes.func.isRequired, startRejectRequest: PropTypes.func.isRequired - }).isRequired + }).isRequired, + isTest: PropTypes.bool.isRequired }; render () { @@ -70,7 +71,7 @@ class Embedded extends Component { } renderPending = (data) => { - const { actions } = this.props; + const { actions, isTest } = this.props; const { payload, id, isSending, date } = data; return ( @@ -83,6 +84,7 @@ class Embedded extends Component { id={ id } payload={ payload } date={ date } + isTest={ isTest } /> ); } @@ -93,11 +95,13 @@ class Embedded extends Component { } function mapStateToProps (state) { + const { isTest } = state.nodeStatus; const { actions, signer } = state; return { actions, - signer + signer, + isTest }; } diff --git a/js/src/views/Signer/containers/RequestsPage/RequestsPage.js b/js/src/views/Signer/containers/RequestsPage/RequestsPage.js index 2a0d087e3..bf4968610 100644 --- a/js/src/views/Signer/containers/RequestsPage/RequestsPage.js +++ b/js/src/views/Signer/containers/RequestsPage/RequestsPage.js @@ -35,7 +35,8 @@ class RequestsPage extends Component { actions: PropTypes.shape({ startConfirmRequest: PropTypes.func.isRequired, startRejectRequest: PropTypes.func.isRequired - }).isRequired + }).isRequired, + isTest: PropTypes.bool.isRequired }; render () { @@ -96,7 +97,7 @@ class RequestsPage extends Component { } renderPending = (data) => { - const { actions } = this.props; + const { actions, isTest } = this.props; const { payload, id, isSending, date } = data; return ( @@ -109,11 +110,13 @@ class RequestsPage extends Component { id={ id } payload={ payload } date={ date } + isTest={ isTest } /> ); } renderFinished = (data) => { + const { isTest } = this.props; const { payload, id, result, msg, status, error, date } = data; return ( @@ -127,6 +130,7 @@ class RequestsPage extends Component { error={ error } payload={ payload } date={ date } + isTest={ isTest } /> ); } @@ -143,7 +147,14 @@ class RequestsPage extends Component { } function mapStateToProps (state) { - return state; + const { isTest } = state.nodeStatus; + const { actions, signer } = state; + + return { + actions, + signer, + isTest + }; } function mapDispatchToProps (dispatch) { From 4498fd54a424a65069e7912d1a4bd772308822ef Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 18 Nov 2016 21:55:55 +0100 Subject: [PATCH 15/97] Typo slipped through --- .../components/TransactionMainDetails/TransactionMainDetails.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/views/Signer/components/TransactionMainDetails/TransactionMainDetails.js b/js/src/views/Signer/components/TransactionMainDetails/TransactionMainDetails.js index 4efa567d2..f86150e48 100644 --- a/js/src/views/Signer/components/TransactionMainDetails/TransactionMainDetails.js +++ b/js/src/views/Signer/components/TransactionMainDetails/TransactionMainDetails.js @@ -30,7 +30,7 @@ export default class TransactionMainDetails extends Component { fromBalance: PropTypes.object, // eth BigNumber, not required since it might take time to fetch value: PropTypes.object.isRequired, // wei hex totalValue: PropTypes.object.isRequired, // wei BigNumber - isTest: PropTypes.isTest.isRequired, + isTest: PropTypes.bool.isRequired, to: PropTypes.string, // undefined if it's a contract toBalance: PropTypes.object, // eth BigNumber - undefined if it's a contract or until it's fetched className: PropTypes.string, From d1e6b9ec5eec1302af8b71ec8a8d14e839c135ec Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 18 Nov 2016 22:26:52 +0100 Subject: [PATCH 16/97] Introduce mobx store for component balances --- .../RequestFinishedWeb3.js | 7 ++- .../RequestPendingWeb3/RequestPendingWeb3.js | 7 ++- .../components/SignRequest/SignRequest.js | 30 ++++------- .../TransactionFinished.css | 1 + .../TransactionFinished.js | 52 ++++--------------- .../TransactionPending/TransactionPending.js | 41 +++++---------- .../Signer/containers/Embedded/embedded.js | 8 +++ .../containers/RequestsPage/RequestsPage.js | 9 ++++ js/src/views/Signer/store.js | 48 +++++++++++++++++ 9 files changed, 110 insertions(+), 93 deletions(-) create mode 100644 js/src/views/Signer/store.js diff --git a/js/src/views/Signer/components/RequestFinishedWeb3/RequestFinishedWeb3.js b/js/src/views/Signer/components/RequestFinishedWeb3/RequestFinishedWeb3.js index 74a20f625..f263a5d77 100644 --- a/js/src/views/Signer/components/RequestFinishedWeb3/RequestFinishedWeb3.js +++ b/js/src/views/Signer/components/RequestFinishedWeb3/RequestFinishedWeb3.js @@ -32,11 +32,12 @@ export default class RequestFinishedWeb3 extends Component { status: PropTypes.string, error: PropTypes.string, className: PropTypes.string, - isTest: PropTypes.bool.isRequired + isTest: PropTypes.bool.isRequired, + store: PropTypes.object.isRequired } render () { - const { payload, id, result, msg, status, error, date, className, isTest } = this.props; + const { payload, id, result, msg, status, error, date, className, isTest, store } = this.props; if (payload.sign) { const { sign } = payload; @@ -52,6 +53,7 @@ export default class RequestFinishedWeb3 extends Component { status={ status } error={ error } isTest={ isTest } + store={ store } /> ); } @@ -74,6 +76,7 @@ export default class RequestFinishedWeb3 extends Component { status={ status } error={ error } isTest={ isTest } + store={ store } /> ); } diff --git a/js/src/views/Signer/components/RequestPendingWeb3/RequestPendingWeb3.js b/js/src/views/Signer/components/RequestPendingWeb3/RequestPendingWeb3.js index d9e695dbc..923cc7970 100644 --- a/js/src/views/Signer/components/RequestPendingWeb3/RequestPendingWeb3.js +++ b/js/src/views/Signer/components/RequestPendingWeb3/RequestPendingWeb3.js @@ -31,7 +31,8 @@ export default class RequestPendingWeb3 extends Component { PropTypes.shape({ sign: PropTypes.object.isRequired }) ]).isRequired, className: PropTypes.string, - isTest: PropTypes.bool.isRequired + isTest: PropTypes.bool.isRequired, + store: PropTypes.object.isRequired }; onConfirm = data => { @@ -42,7 +43,7 @@ export default class RequestPendingWeb3 extends Component { }; render () { - const { payload, id, className, isSending, date, onReject, isTest } = this.props; + const { payload, id, className, isSending, date, onReject, isTest, store } = this.props; if (payload.sign) { const { sign } = payload; @@ -58,6 +59,7 @@ export default class RequestPendingWeb3 extends Component { address={ sign.address } hash={ sign.hash } isTest={ isTest } + store={ store } /> ); } @@ -80,6 +82,7 @@ export default class RequestPendingWeb3 extends Component { value={ transaction.value } date={ date } isTest={ isTest } + store={ store } /> ); } diff --git a/js/src/views/Signer/components/SignRequest/SignRequest.js b/js/src/views/Signer/components/SignRequest/SignRequest.js index 6fb2b37b7..ae4159c71 100644 --- a/js/src/views/Signer/components/SignRequest/SignRequest.js +++ b/js/src/views/Signer/components/SignRequest/SignRequest.js @@ -15,7 +15,7 @@ // along with Parity. If not, see . import React, { Component, PropTypes } from 'react'; -import nullable from '../../../../util/nullable-proptype'; +import { observer } from 'mobx-react'; import Account from '../Account'; import TransactionPendingForm from '../TransactionPendingForm'; @@ -23,12 +23,8 @@ import TxHashLink from '../TxHashLink'; import styles from './SignRequest.css'; +@observer export default class SignRequest extends Component { - static contextTypes = { - api: PropTypes.object - } - - // TODO [todr] re-use proptypes? static propTypes = { id: PropTypes.object.isRequired, address: PropTypes.string.isRequired, @@ -40,21 +36,13 @@ export default class SignRequest extends Component { status: PropTypes.string, className: PropTypes.string, isTest: PropTypes.bool.isRequired, - balance: nullable(PropTypes.object) + store: PropTypes.object.isRequired }; - state = { - balance: null - } - componentWillMount () { - this.context.api.eth.getBalance(this.props.address) - .then((balance) => { - this.setState({ balance }); - }) - .catch((err) => { - console.error('could not fetch balance', err); - }); + const { address, store } = this.props; + + store.fetchBalance(address); } render () { @@ -69,8 +57,8 @@ export default class SignRequest extends Component { } renderDetails () { - const { address, hash, isTest } = this.props; - const { balance } = this.state; + const { address, hash, isTest, store } = this.props; + const balance = store.balances[address]; if (!balance) { return
; @@ -133,11 +121,11 @@ export default class SignRequest extends Component { onConfirm = password => { const { id } = this.props; + this.props.onConfirm({ id, password }); } onReject = () => { this.props.onReject(this.props.id); } - } diff --git a/js/src/views/Signer/components/TransactionFinished/TransactionFinished.css b/js/src/views/Signer/components/TransactionFinished/TransactionFinished.css index 91749cc61..0db00d7e4 100644 --- a/js/src/views/Signer/components/TransactionFinished/TransactionFinished.css +++ b/js/src/views/Signer/components/TransactionFinished/TransactionFinished.css @@ -14,6 +14,7 @@ /* You should have received a copy of the GNU General Public License /* along with Parity. If not, see . */ + .container { padding: 25px 0 15px; } diff --git a/js/src/views/Signer/components/TransactionFinished/TransactionFinished.js b/js/src/views/Signer/components/TransactionFinished/TransactionFinished.js index 54333115d..dcfdb5e00 100644 --- a/js/src/views/Signer/components/TransactionFinished/TransactionFinished.js +++ b/js/src/views/Signer/components/TransactionFinished/TransactionFinished.js @@ -15,9 +15,7 @@ // along with Parity. If not, see . import React, { Component, PropTypes } from 'react'; -import nullable from '../../../../util/nullable-proptype'; - -import CircularProgress from 'material-ui/CircularProgress'; +import { observer } from 'mobx-react'; import { TxHash } from '../../../../ui'; @@ -30,11 +28,8 @@ import styles from './TransactionFinished.css'; import * as tUtil from '../util/transaction'; import { capitalize } from '../util/util'; +@observer export default class TransactionFinished extends Component { - static contextTypes = { - api: PropTypes.object.isRequired - }; - static propTypes = { id: PropTypes.object.isRequired, from: PropTypes.string.isRequired, @@ -48,39 +43,23 @@ export default class TransactionFinished extends Component { className: PropTypes.string, data: PropTypes.string, isTest: PropTypes.bool.isRequired, - fromBalance: nullable(PropTypes.object), - toBalance: nullable(PropTypes.object) - }; - - state = { - fromBalance: null, - toBalance: null + store: PropTypes.object.isRequired }; componentWillMount () { - const { from, to, gas, gasPrice, value } = this.props; + const { from, to, gas, gasPrice, value, store } = this.props; const fee = tUtil.getFee(gas, gasPrice); // BigNumber object const totalValue = tUtil.getTotalValue(fee, value); - this.setState({ totalValue }); - this.fetchBalance(from, 'fromBalance'); - if (to) { - this.fetchBalance(to, 'toBalance'); - } + this.setState({ totalValue }); + store.fetchBalances([from, to]); } render () { - const { fromBalance, toBalance } = this.state; + const { className, date, id, from, to, store } = this.props; - if (!fromBalance || !toBalance) { - return ( -
- -
- ); - } - - const { className, date, id } = this.props; + const fromBalance = store.balances[from]; + const toBalance = store.balances[to]; return (
@@ -88,6 +67,8 @@ export default class TransactionFinished extends Component { ); } - - fetchBalance (address, key) { - this.context.api.eth.getBalance(address) - .then((balance) => { - this.setState({ [key]: balance }); - }) - .catch((err) => { - console.error('could not fetch balance', err); - }); - } - } diff --git a/js/src/views/Signer/components/TransactionPending/TransactionPending.js b/js/src/views/Signer/components/TransactionPending/TransactionPending.js index 55f09ac97..0742c2c76 100644 --- a/js/src/views/Signer/components/TransactionPending/TransactionPending.js +++ b/js/src/views/Signer/components/TransactionPending/TransactionPending.js @@ -15,6 +15,7 @@ // along with Parity. If not, see . import React, { Component, PropTypes } from 'react'; +import { observer } from 'mobx-react'; import TransactionMainDetails from '../TransactionMainDetails'; import TransactionPendingForm from '../TransactionPendingForm'; @@ -24,11 +25,8 @@ import styles from './TransactionPending.css'; import * as tUtil from '../util/transaction'; +@observer export default class TransactionPending extends Component { - static contextTypes = { - api: PropTypes.object.isRequired - }; - static propTypes = { id: PropTypes.object.isRequired, from: PropTypes.string.isRequired, @@ -43,34 +41,32 @@ export default class TransactionPending extends Component { onReject: PropTypes.func.isRequired, isSending: PropTypes.bool.isRequired, className: PropTypes.string, - isTest: PropTypes.bool.isRequired + isTest: PropTypes.bool.isRequired, + store: PropTypes.object.isRequired }; static defaultProps = { isSending: false }; - state = { - fromBalance: null, - toBalance: null - }; - componentWillMount () { - const { gas, gasPrice, value } = this.props; + const { gas, gasPrice, value, from, to, store } = this.props; + const fee = tUtil.getFee(gas, gasPrice); // BigNumber object const totalValue = tUtil.getTotalValue(fee, value); const gasPriceEthmDisplay = tUtil.getEthmFromWeiDisplay(gasPrice); const gasToDisplay = tUtil.getGasDisplay(gas); - this.setState({ gasPriceEthmDisplay, totalValue, gasToDisplay }); - const { from, to } = this.props; - this.fetchBalance(from, 'fromBalance'); - if (to) this.fetchBalance(to, 'toBalance'); + this.setState({ gasPriceEthmDisplay, totalValue, gasToDisplay }); + store.fetchBalances([from, to]); } render () { + const { className, id, date, data, from, to, store } = this.props; const { totalValue, gasPriceEthmDisplay, gasToDisplay } = this.state; - const { className, id, date, data, from } = this.props; + + const fromBalance = store.balances[from]; + const toBalance = store.balances[to]; return (
@@ -78,6 +74,8 @@ export default class TransactionPending extends Component { { this.props.onReject(this.props.id); } - - fetchBalance (address, key) { - this.context.api.eth.getBalance(address) - .then((balance) => { - this.setState({ [key]: balance }); - }) - .catch((err) => { - console.error('could not fetch balance', err); - }); - } - } diff --git a/js/src/views/Signer/containers/Embedded/embedded.js b/js/src/views/Signer/containers/Embedded/embedded.js index e06eaf274..af0bd4bfa 100644 --- a/js/src/views/Signer/containers/Embedded/embedded.js +++ b/js/src/views/Signer/containers/Embedded/embedded.js @@ -19,6 +19,7 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; +import Store from '../../store'; import * as RequestsActions from '../../../../redux/providers/signerActions'; import { Container } from '../../../../ui'; @@ -27,6 +28,10 @@ import { RequestPendingWeb3 } from '../../components'; import styles from './embedded.css'; class Embedded extends Component { + static contextTypes = { + api: PropTypes.object.isRequired + }; + static propTypes = { signer: PropTypes.shape({ pending: PropTypes.array.isRequired, @@ -39,6 +44,8 @@ class Embedded extends Component { isTest: PropTypes.bool.isRequired }; + store = new Store(this.context.api); + render () { return ( @@ -85,6 +92,7 @@ class Embedded extends Component { payload={ payload } date={ date } isTest={ isTest } + store={ this.store } /> ); } diff --git a/js/src/views/Signer/containers/RequestsPage/RequestsPage.js b/js/src/views/Signer/containers/RequestsPage/RequestsPage.js index bf4968610..5aab249cc 100644 --- a/js/src/views/Signer/containers/RequestsPage/RequestsPage.js +++ b/js/src/views/Signer/containers/RequestsPage/RequestsPage.js @@ -19,6 +19,7 @@ import React, { Component, PropTypes } from 'react'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; +import Store from '../../store'; import * as RequestsActions from '../../../../redux/providers/signerActions'; import { Container, ContainerTitle } from '../../../../ui'; @@ -27,6 +28,10 @@ import { RequestPendingWeb3, RequestFinishedWeb3 } from '../../components'; import styles from './RequestsPage.css'; class RequestsPage extends Component { + static contextTypes = { + api: PropTypes.object.isRequired + }; + static propTypes = { signer: PropTypes.shape({ pending: PropTypes.array.isRequired, @@ -39,6 +44,8 @@ class RequestsPage extends Component { isTest: PropTypes.bool.isRequired }; + store = new Store(this.context.api); + render () { const { pending, finished } = this.props.signer; @@ -111,6 +118,7 @@ class RequestsPage extends Component { payload={ payload } date={ date } isTest={ isTest } + store={ this.store } /> ); } @@ -131,6 +139,7 @@ class RequestsPage extends Component { payload={ payload } date={ date } isTest={ isTest } + store={ this.store } /> ); } diff --git a/js/src/views/Signer/store.js b/js/src/views/Signer/store.js new file mode 100644 index 000000000..ce12cd267 --- /dev/null +++ b/js/src/views/Signer/store.js @@ -0,0 +1,48 @@ +// 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 . + +import { action, observable } from 'mobx'; + +export default class Store { + @observable balances = {}; + + constructor (api) { + this._api = api; + } + + @action setBalance = (address, balance) => { + this.balances = Object.assign({}, this.balances, { [address]: balance }); + } + + fetchBalance (address) { + this._api.eth + .getBalance(address) + .then((balance) => { + this.setBalance(address, balance); + }) + .catch((error) => { + console.warn('Store:fetchBalance', error); + }); + } + + fetchBalances (addresses) { + addresses.forEach((address) => { + if (address) { + this.fetBalance(address); + } + }); + } +} From 1c4779683fe0d84a4f49cd347703a0c3fc694e16 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 18 Nov 2016 22:30:36 +0100 Subject: [PATCH 17/97] Rename Request{Pending,Finished}Web3 -> Request{Pending,Finished} --- .../{RequestPendingWeb3 => RequestFinished}/index.js | 2 +- .../requestFinished.js} | 2 +- .../{RequestFinishedWeb3 => RequestPending}/index.js | 2 +- .../requestPending.js} | 2 +- js/src/views/Signer/components/index.js | 4 ++-- js/src/views/Signer/containers/Embedded/embedded.js | 4 ++-- js/src/views/Signer/containers/RequestsPage/RequestsPage.js | 6 +++--- 7 files changed, 11 insertions(+), 11 deletions(-) rename js/src/views/Signer/components/{RequestPendingWeb3 => RequestFinished}/index.js (94%) rename js/src/views/Signer/components/{RequestFinishedWeb3/RequestFinishedWeb3.js => RequestFinished/requestFinished.js} (97%) rename js/src/views/Signer/components/{RequestFinishedWeb3 => RequestPending}/index.js (93%) rename js/src/views/Signer/components/{RequestPendingWeb3/RequestPendingWeb3.js => RequestPending/requestPending.js} (97%) diff --git a/js/src/views/Signer/components/RequestPendingWeb3/index.js b/js/src/views/Signer/components/RequestFinished/index.js similarity index 94% rename from js/src/views/Signer/components/RequestPendingWeb3/index.js rename to js/src/views/Signer/components/RequestFinished/index.js index f664b571c..c5ed83b6b 100644 --- a/js/src/views/Signer/components/RequestPendingWeb3/index.js +++ b/js/src/views/Signer/components/RequestFinished/index.js @@ -14,4 +14,4 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -export default from './RequestPendingWeb3'; +export default from './requestFinished'; diff --git a/js/src/views/Signer/components/RequestFinishedWeb3/RequestFinishedWeb3.js b/js/src/views/Signer/components/RequestFinished/requestFinished.js similarity index 97% rename from js/src/views/Signer/components/RequestFinishedWeb3/RequestFinishedWeb3.js rename to js/src/views/Signer/components/RequestFinished/requestFinished.js index f263a5d77..bce9e4038 100644 --- a/js/src/views/Signer/components/RequestFinishedWeb3/RequestFinishedWeb3.js +++ b/js/src/views/Signer/components/RequestFinished/requestFinished.js @@ -19,7 +19,7 @@ import React, { Component, PropTypes } from 'react'; import TransactionFinished from '../TransactionFinished'; import SignRequest from '../SignRequest'; -export default class RequestFinishedWeb3 extends Component { +export default class RequestFinished extends Component { static propTypes = { id: PropTypes.object.isRequired, result: PropTypes.any.isRequired, diff --git a/js/src/views/Signer/components/RequestFinishedWeb3/index.js b/js/src/views/Signer/components/RequestPending/index.js similarity index 93% rename from js/src/views/Signer/components/RequestFinishedWeb3/index.js rename to js/src/views/Signer/components/RequestPending/index.js index bcf7341bb..d4b048781 100644 --- a/js/src/views/Signer/components/RequestFinishedWeb3/index.js +++ b/js/src/views/Signer/components/RequestPending/index.js @@ -14,4 +14,4 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -export default from './RequestFinishedWeb3'; +export default from './requestPending'; diff --git a/js/src/views/Signer/components/RequestPendingWeb3/RequestPendingWeb3.js b/js/src/views/Signer/components/RequestPending/requestPending.js similarity index 97% rename from js/src/views/Signer/components/RequestPendingWeb3/RequestPendingWeb3.js rename to js/src/views/Signer/components/RequestPending/requestPending.js index 923cc7970..d8e2e0565 100644 --- a/js/src/views/Signer/components/RequestPendingWeb3/RequestPendingWeb3.js +++ b/js/src/views/Signer/components/RequestPending/requestPending.js @@ -19,7 +19,7 @@ import React, { Component, PropTypes } from 'react'; import TransactionPending from '../TransactionPending'; import SignRequest from '../SignRequest'; -export default class RequestPendingWeb3 extends Component { +export default class RequestPending extends Component { static propTypes = { id: PropTypes.object.isRequired, onConfirm: PropTypes.func.isRequired, diff --git a/js/src/views/Signer/components/index.js b/js/src/views/Signer/components/index.js index 2dd5174e2..7c891f621 100644 --- a/js/src/views/Signer/components/index.js +++ b/js/src/views/Signer/components/index.js @@ -14,5 +14,5 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -export RequestFinishedWeb3 from './RequestFinishedWeb3'; -export RequestPendingWeb3 from './RequestPendingWeb3'; +export RequestFinished from './RequestFinished'; +export RequestPending from './RequestPending'; diff --git a/js/src/views/Signer/containers/Embedded/embedded.js b/js/src/views/Signer/containers/Embedded/embedded.js index af0bd4bfa..b62c1a6c0 100644 --- a/js/src/views/Signer/containers/Embedded/embedded.js +++ b/js/src/views/Signer/containers/Embedded/embedded.js @@ -23,7 +23,7 @@ import Store from '../../store'; import * as RequestsActions from '../../../../redux/providers/signerActions'; import { Container } from '../../../../ui'; -import { RequestPendingWeb3 } from '../../components'; +import { RequestPending } from '../../components'; import styles from './embedded.css'; @@ -82,7 +82,7 @@ class Embedded extends Component { const { payload, id, isSending, date } = data; return ( - Date: Sun, 20 Nov 2016 16:17:57 +0100 Subject: [PATCH 18/97] RPC for deleting accounts. --- ethcore/src/account_provider.rs | 10 ++++++++-- rpc/src/v1/impls/parity_accounts.rs | 11 ++++++++++- rpc/src/v1/traits/parity_accounts.rs | 5 +++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/ethcore/src/account_provider.rs b/ethcore/src/account_provider.rs index e906aefe9..917ae8b8b 100644 --- a/ethcore/src/account_provider.rs +++ b/ethcore/src/account_provider.rs @@ -276,14 +276,20 @@ impl AccountProvider { } /// Returns `true` if the password for `account` is `password`. `false` if not. - pub fn test_password(&self, account: &Address, password: String) -> Result { - match self.sstore.sign(account, &password, &Default::default()) { + pub fn test_password(&self, account: &Address, password: &str) -> Result { + match self.sstore.sign(account, password, &Default::default()) { Ok(_) => Ok(true), Err(SSError::InvalidPassword) => Ok(false), Err(e) => Err(Error::SStore(e)), } } + /// Permanently removes an account. + pub fn kill_account(&self, account: &Address, password: &str) -> Result<(), Error> { + try!(self.sstore.remove_account(account, &password)); + Ok(()) + } + /// Changes the password of `account` from `password` to `new_password`. Fails if incorrect `password` given. pub fn change_password(&self, account: &Address, password: String, new_password: String) -> Result<(), Error> { self.sstore.change_password(account, &password, &new_password).map_err(Error::SStore) diff --git a/rpc/src/v1/impls/parity_accounts.rs b/rpc/src/v1/impls/parity_accounts.rs index 2644c59e3..8229715ea 100644 --- a/rpc/src/v1/impls/parity_accounts.rs +++ b/rpc/src/v1/impls/parity_accounts.rs @@ -104,7 +104,7 @@ impl ParityAccounts for ParityAccountsClient where C: MiningBlock let account: Address = account.into(); take_weak!(self.accounts) - .test_password(&account, password) + .test_password(&account, &password) .map_err(|e| errors::account("Could not fetch account info.", e)) } @@ -117,6 +117,15 @@ impl ParityAccounts for ParityAccountsClient where C: MiningBlock .map_err(|e| errors::account("Could not fetch account info.", e)) } + fn kill_account(&self, account: RpcH160, password: String) -> Result { + try!(self.active()); + let account: Address = account.into(); + take_weak!(self.accounts) + .kill_account(&account, &password) + .map(|_| true) + .map_err(|e| errors::account("Could not fetch account info.", e)) + } + fn set_account_name(&self, addr: RpcH160, name: String) -> Result { try!(self.active()); let store = take_weak!(self.accounts); diff --git a/rpc/src/v1/traits/parity_accounts.rs b/rpc/src/v1/traits/parity_accounts.rs index 0f62f59d1..29706d0b2 100644 --- a/rpc/src/v1/traits/parity_accounts.rs +++ b/rpc/src/v1/traits/parity_accounts.rs @@ -53,6 +53,11 @@ build_rpc_trait! { #[rpc(name = "parity_changePassword")] fn change_password(&self, H160, String, String) -> Result; + /// Permanently deletes an account. + /// Arguments: `account`, `password`. + #[rpc(name = "parity_killAccount")] + fn kill_account(&self, H160, String) -> Result; + /// Set an account's name. #[rpc(name = "parity_setAccountName")] fn set_account_name(&self, H160, String) -> Result; From 2d9369e5ba150899ae3b94d8878daae8a2fc09f9 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 21 Nov 2016 11:03:18 +0100 Subject: [PATCH 19/97] Introduce test. --- rpc/src/v1/tests/mocked/parity_accounts.rs | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/rpc/src/v1/tests/mocked/parity_accounts.rs b/rpc/src/v1/tests/mocked/parity_accounts.rs index c5ed4172e..c0982e60c 100644 --- a/rpc/src/v1/tests/mocked/parity_accounts.rs +++ b/rpc/src/v1/tests/mocked/parity_accounts.rs @@ -116,3 +116,27 @@ fn should_be_able_to_set_meta() { assert_eq!(res, Some(response)); } +#[test] +fn should_be_able_to_kill_account() { + let tester = setup(); + tester.accounts.new_account("password").unwrap(); + let accounts = tester.accounts.accounts().unwrap(); + assert_eq!(accounts.len(), 1); + let address = accounts[0]; + + let request = format!(r#"{{"jsonrpc": "2.0", "method": "parity_killAccount", "params": ["0xf00baba2f00baba2f00baba2f00baba2f00baba2"], "id": 1}}"#); + let response = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid params","data":null},"id":1}"#; + let res = tester.io.handle_request_sync(&request); + assert_eq!(res, Some(response.into())); + + let request = format!(r#"{{"jsonrpc": "2.0", "method": "parity_killAccount", "params": ["0x{}", "password"], "id": 1}}"#, address.hex()); + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + let res = tester.io.handle_request_sync(&request); + assert_eq!(res, Some(response.into())); + + let request = r#"{"jsonrpc": "2.0", "method": "parity_accountsInfo", "params": [], "id": 1}"#; + let res = tester.io.handle_request_sync(request); + let response = format!("{{\"jsonrpc\":\"2.0\",\"result\":{{}},\"id\":1}}"); + assert_eq!(res, Some(response)); +} + From d48391d6be7195408b0aaa0d54053aceaf80a428 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Mon, 21 Nov 2016 12:26:38 +0100 Subject: [PATCH 20/97] ropsten --- js/src/redux/providers/status.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/redux/providers/status.js b/js/src/redux/providers/status.js index ed7a2cbe0..bdc4dd38b 100644 --- a/js/src/redux/providers/status.js +++ b/js/src/redux/providers/status.js @@ -251,7 +251,7 @@ export default class Status { .then(([ clientVersion, defaultExtraData, netChain, netPort, rpcSettings, enode ]) => { - const isTest = netChain === 'morden' || netChain === 'testnet'; + const isTest = netChain === 'morden' || netChain === 'ropsten' || netChain === 'testnet'; const longStatus = { clientVersion, From 278b1e3bd50a5668d185ce094c439258dd8dcaed Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Mon, 21 Nov 2016 13:04:32 +0100 Subject: [PATCH 21/97] ropsten updates as per comment --- js/src/dapps/basiccoin/services.js | 2 +- js/src/redux/providers/statusReducer.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/dapps/basiccoin/services.js b/js/src/dapps/basiccoin/services.js index 4aed4199f..3dd5202a7 100644 --- a/js/src/dapps/basiccoin/services.js +++ b/js/src/dapps/basiccoin/services.js @@ -105,7 +105,7 @@ export function attachInstances () { ]) .then(([registryAddress, netChain]) => { const registry = api.newContract(abis.registry, registryAddress).instance; - isTest = netChain === 'morden' || netChain === 'testnet'; + isTest = ['morden', 'ropsten', 'testnet'].includes(netChain); console.log(`contract was found at registry=${registryAddress}`); console.log(`running on ${netChain}, isTest=${isTest}`); diff --git a/js/src/redux/providers/statusReducer.js b/js/src/redux/providers/statusReducer.js index f0b6947c0..07fa993b9 100644 --- a/js/src/redux/providers/statusReducer.js +++ b/js/src/redux/providers/statusReducer.js @@ -31,7 +31,7 @@ const initialState = { gasLimit: new BigNumber(0), hashrate: new BigNumber(0), minGasPrice: new BigNumber(0), - netChain: 'morden', + netChain: 'ropsten', netPeers: { active: new BigNumber(0), connected: new BigNumber(0), From c6db1575542b5c33f635dfa95356d11d57bba1fb Mon Sep 17 00:00:00 2001 From: keorn Date: Mon, 21 Nov 2016 12:06:14 +0000 Subject: [PATCH 22/97] fix flaky test, clean up specs --- ethcore/res/authority_round.json | 4 ++-- ethcore/res/instant_seal.json | 6 +++--- ethcore/src/engines/authority_round.rs | 14 +++++++------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ethcore/res/authority_round.json b/ethcore/res/authority_round.json index ad23b461f..9ab782395 100644 --- a/ethcore/res/authority_round.json +++ b/ethcore/res/authority_round.json @@ -21,8 +21,8 @@ "genesis": { "seal": { "generic": { - "fields": 1, - "rlp": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa" + "fields": 2, + "rlp": "0x200" } }, "difficulty": "0x20000", diff --git a/ethcore/res/instant_seal.json b/ethcore/res/instant_seal.json index 2d5b38659..a6b24faf9 100644 --- a/ethcore/res/instant_seal.json +++ b/ethcore/res/instant_seal.json @@ -11,9 +11,9 @@ }, "genesis": { "seal": { - "ethereum": { - "nonce": "0x00006d6f7264656e", - "mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578" + "generic": { + "fields": 0, + "rlp": "0x0" } }, "difficulty": "0x20000", diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index 9bed99e8b..8bd4156d7 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -254,8 +254,8 @@ impl Engine for AuthorityRound { /// Check if the signature belongs to the correct proposer. fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { - let header_step = try!(header_step(header)); - // Give one step slack if step is lagging, double vote is still not possible. + let header_step = try!(header_step(header)); + // Give one step slack if step is lagging, double vote is still not possible. if header_step <= self.step() + 1 { let proposer_signature = try!(header_signature(header)); let ok_sig = try!(verify_address(self.step_proposer(header_step), &proposer_signature, &header.bare_hash())); @@ -417,13 +417,13 @@ mod tests { let engine = Spec::new_test_round().engine; let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap(); - let mut step = UNIX_EPOCH.elapsed().unwrap().as_secs(); + let time = UNIX_EPOCH.elapsed().unwrap().as_secs(); + // Two authorities. + let mut step = time - time % 2; header.set_seal(vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); - let first_ok = engine.verify_block_seal(&header).is_ok(); + assert!(engine.verify_block_seal(&header).is_err()); step = step + 1; header.set_seal(vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); - let second_ok = engine.verify_block_seal(&header).is_ok(); - - assert!(first_ok ^ second_ok); + assert!(engine.verify_block_seal(&header).is_ok()); } } From 959ccc705a81aa912ade59f5125a30f7b9997ee3 Mon Sep 17 00:00:00 2001 From: keorn Date: Mon, 21 Nov 2016 12:07:43 +0000 Subject: [PATCH 23/97] fix indent --- ethcore/src/engines/authority_round.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index 8bd4156d7..830fcf9c8 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -255,7 +255,7 @@ impl Engine for AuthorityRound { /// Check if the signature belongs to the correct proposer. fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { let header_step = try!(header_step(header)); - // Give one step slack if step is lagging, double vote is still not possible. + // Give one step slack if step is lagging, double vote is still not possible. if header_step <= self.step() + 1 { let proposer_signature = try!(header_signature(header)); let ok_sig = try!(verify_address(self.step_proposer(header_step), &proposer_signature, &header.bare_hash())); From ca5fd0b23d5616ac523e7825bfe526db47fd4787 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Mon, 21 Nov 2016 14:14:25 +0100 Subject: [PATCH 24/97] React Perf in dev mode // Smarter background component #3240 --- js/package.json | 1 + js/src/index.js | 6 +++ .../ui/ParityBackground/parityBackground.js | 43 ++++++++++--------- .../views/Application/Container/container.js | 7 ++- js/src/views/ParityBar/parityBar.js | 11 ++++- .../views/Settings/Background/background.js | 5 ++- 6 files changed, 49 insertions(+), 24 deletions(-) diff --git a/js/package.json b/js/package.json index 7f4157cad..f8705f1a1 100644 --- a/js/package.json +++ b/js/package.json @@ -102,6 +102,7 @@ "postcss-nested": "^1.0.0", "postcss-simple-vars": "^3.0.0", "raw-loader": "^0.5.1", + "react-addons-perf": "~15.3.2", "react-addons-test-utils": "~15.3.2", "react-copy-to-clipboard": "^4.2.3", "react-dom": "~15.3.2", diff --git a/js/src/index.js b/js/src/index.js index fda785842..0e0433c1e 100644 --- a/js/src/index.js +++ b/js/src/index.js @@ -46,6 +46,12 @@ import './index.html'; injectTapEventPlugin(); +if (process.env.NODE_ENV === 'development') { + // Expose the React Performance Tools on the`window` object + const Perf = require('react-addons-perf'); + window.Perf = Perf; +} + const AUTH_HASH = '#/auth?'; const parityUrl = process.env.PARITY_URL || ( diff --git a/js/src/ui/ParityBackground/parityBackground.js b/js/src/ui/ParityBackground/parityBackground.js index 0916d3a85..5198195c0 100644 --- a/js/src/ui/ParityBackground/parityBackground.js +++ b/js/src/ui/ParityBackground/parityBackground.js @@ -16,26 +16,17 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; class ParityBackground extends Component { - static contextTypes = { - muiTheme: PropTypes.object.isRequired - } - static propTypes = { + style: PropTypes.object.isRequired, children: PropTypes.node, className: PropTypes.string, - gradient: PropTypes.string, - seed: PropTypes.any, - settings: PropTypes.object.isRequired, onClick: PropTypes.func - } + }; render () { - const { muiTheme } = this.context; - const { children, className, gradient, seed, settings, onClick } = this.props; - const style = muiTheme.parity.getBackgroundStyle(gradient, seed || settings.backgroundSeed); + const { children, className, style, onClick } = this.props; return (
{ + const { backgroundSeed } = state.settings; + const { seed } = props; + + const newSeed = seed || backgroundSeed; + + if (newSeed === _seed) { + return _props; + } + + _seed = newSeed; + _props = { style: muiTheme.parity.getBackgroundStyle(gradient, newSeed) }; + + return _props; + }; } export default connect( - mapStateToProps, - mapDispatchToProps + mapStateToProps )(ParityBackground); diff --git a/js/src/views/Application/Container/container.js b/js/src/views/Application/Container/container.js index a1b9124c7..d3908f570 100644 --- a/js/src/views/Application/Container/container.js +++ b/js/src/views/Application/Container/container.js @@ -22,6 +22,10 @@ import { Errors, ParityBackground, Tooltips } from '../../../ui'; import styles from '../application.css'; export default class Container extends Component { + static contextTypes = { + muiTheme: PropTypes.object.isRequired + }; + static propTypes = { children: PropTypes.node.isRequired, showFirstRun: PropTypes.bool, @@ -30,9 +34,10 @@ export default class Container extends Component { render () { const { children, showFirstRun, onCloseFirstRun } = this.props; + const { muiTheme } = this.context; return ( - + diff --git a/js/src/views/ParityBar/parityBar.js b/js/src/views/ParityBar/parityBar.js index 0f3380ca0..40fe659ad 100644 --- a/js/src/views/ParityBar/parityBar.js +++ b/js/src/views/ParityBar/parityBar.js @@ -28,6 +28,10 @@ import imagesEthcoreBlock from '../../../assets/images/parity-logo-white-no-text import styles from './parityBar.css'; class ParityBar extends Component { + static contextTypes = { + muiTheme: PropTypes.object.isRequired + }; + static propTypes = { pending: PropTypes.array, dapp: PropTypes.bool @@ -62,6 +66,7 @@ class ParityBar extends Component { renderBar () { const { dapp } = this.props; + const { muiTheme } = this.context; if (!dapp) { return null; @@ -75,7 +80,7 @@ class ParityBar extends Component { return (
- +
); } From af0db9f80eecf88577c86253166fdaf8ce5cc713 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Mon, 21 Nov 2016 18:14:23 +0100 Subject: [PATCH 34/97] New registry contract address for ropsten (#3549) * New registry for ropsten * Registry address * Registry with payable modifier --- ethcore/res/ethereum/ropsten.json | 504 +++++++++++++++--------------- 1 file changed, 252 insertions(+), 252 deletions(-) diff --git a/ethcore/res/ethereum/ropsten.json b/ethcore/res/ethereum/ropsten.json index c92676161..62282801d 100644 --- a/ethcore/res/ethereum/ropsten.json +++ b/ethcore/res/ethereum/ropsten.json @@ -8,7 +8,7 @@ "difficultyBoundDivisor": "0x0800", "durationLimit": "0x0d", "blockReward": "0x4563918244F40000", - "registrar": "0x52dff57a8a1532e6afb3dc07e2af58bb9eb05b3d", + "registrar": "0x81a4b044831c4f12ba601adb9274516939e9b8a2", "homesteadTransition": 0, "eip150Transition": 0, "eip155Transition": 10, @@ -47,258 +47,258 @@ "0000000000000000000000000000000000000002": { "balance": "1", "nonce": "0", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, "0000000000000000000000000000000000000003": { "balance": "1", "nonce": "0", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, "0000000000000000000000000000000000000004": { "balance": "1", "nonce": "0", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, - "0000000000000000000000000000000000000000": { "balance": "1" }, - "0000000000000000000000000000000000000005": { "balance": "1" }, - "0000000000000000000000000000000000000006": { "balance": "1" }, - "0000000000000000000000000000000000000007": { "balance": "1" }, - "0000000000000000000000000000000000000008": { "balance": "1" }, - "0000000000000000000000000000000000000009": { "balance": "1" }, - "000000000000000000000000000000000000000a": { "balance": "0" }, - "000000000000000000000000000000000000000b": { "balance": "0" }, - "000000000000000000000000000000000000000c": { "balance": "0" }, - "000000000000000000000000000000000000000d": { "balance": "0" }, - "000000000000000000000000000000000000000e": { "balance": "0" }, - "000000000000000000000000000000000000000f": { "balance": "0" }, - "0000000000000000000000000000000000000010": { "balance": "0" }, - "0000000000000000000000000000000000000011": { "balance": "0" }, - "0000000000000000000000000000000000000012": { "balance": "0" }, - "0000000000000000000000000000000000000013": { "balance": "0" }, - "0000000000000000000000000000000000000014": { "balance": "0" }, - "0000000000000000000000000000000000000015": { "balance": "0" }, - "0000000000000000000000000000000000000016": { "balance": "0" }, - "0000000000000000000000000000000000000017": { "balance": "0" }, - "0000000000000000000000000000000000000018": { "balance": "0" }, - "0000000000000000000000000000000000000019": { "balance": "0" }, - "000000000000000000000000000000000000001a": { "balance": "0" }, - "000000000000000000000000000000000000001b": { "balance": "0" }, - "000000000000000000000000000000000000001c": { "balance": "0" }, - "000000000000000000000000000000000000001d": { "balance": "0" }, - "000000000000000000000000000000000000001e": { "balance": "0" }, - "000000000000000000000000000000000000001f": { "balance": "0" }, - "0000000000000000000000000000000000000020": { "balance": "0" }, - "0000000000000000000000000000000000000021": { "balance": "0" }, - "0000000000000000000000000000000000000022": { "balance": "0" }, - "0000000000000000000000000000000000000023": { "balance": "0" }, - "0000000000000000000000000000000000000024": { "balance": "0" }, - "0000000000000000000000000000000000000025": { "balance": "0" }, - "0000000000000000000000000000000000000026": { "balance": "0" }, - "0000000000000000000000000000000000000027": { "balance": "0" }, - "0000000000000000000000000000000000000028": { "balance": "0" }, - "0000000000000000000000000000000000000029": { "balance": "0" }, - "000000000000000000000000000000000000002a": { "balance": "0" }, - "000000000000000000000000000000000000002b": { "balance": "0" }, - "000000000000000000000000000000000000002c": { "balance": "0" }, - "000000000000000000000000000000000000002d": { "balance": "0" }, - "000000000000000000000000000000000000002e": { "balance": "0" }, - "000000000000000000000000000000000000002f": { "balance": "0" }, - "0000000000000000000000000000000000000030": { "balance": "0" }, - "0000000000000000000000000000000000000031": { "balance": "0" }, - "0000000000000000000000000000000000000032": { "balance": "0" }, - "0000000000000000000000000000000000000033": { "balance": "0" }, - "0000000000000000000000000000000000000034": { "balance": "0" }, - "0000000000000000000000000000000000000035": { "balance": "0" }, - "0000000000000000000000000000000000000036": { "balance": "0" }, - "0000000000000000000000000000000000000037": { "balance": "0" }, - "0000000000000000000000000000000000000038": { "balance": "0" }, - "0000000000000000000000000000000000000039": { "balance": "0" }, - "000000000000000000000000000000000000003a": { "balance": "0" }, - "000000000000000000000000000000000000003b": { "balance": "0" }, - "000000000000000000000000000000000000003c": { "balance": "0" }, - "000000000000000000000000000000000000003d": { "balance": "0" }, - "000000000000000000000000000000000000003e": { "balance": "0" }, - "000000000000000000000000000000000000003f": { "balance": "0" }, - "0000000000000000000000000000000000000040": { "balance": "0" }, - "0000000000000000000000000000000000000041": { "balance": "0" }, - "0000000000000000000000000000000000000042": { "balance": "0" }, - "0000000000000000000000000000000000000043": { "balance": "0" }, - "0000000000000000000000000000000000000044": { "balance": "0" }, - "0000000000000000000000000000000000000045": { "balance": "0" }, - "0000000000000000000000000000000000000046": { "balance": "0" }, - "0000000000000000000000000000000000000047": { "balance": "0" }, - "0000000000000000000000000000000000000048": { "balance": "0" }, - "0000000000000000000000000000000000000049": { "balance": "0" }, - "000000000000000000000000000000000000004a": { "balance": "0" }, - "000000000000000000000000000000000000004b": { "balance": "0" }, - "000000000000000000000000000000000000004c": { "balance": "0" }, - "000000000000000000000000000000000000004d": { "balance": "0" }, - "000000000000000000000000000000000000004e": { "balance": "0" }, - "000000000000000000000000000000000000004f": { "balance": "0" }, - "0000000000000000000000000000000000000050": { "balance": "0" }, - "0000000000000000000000000000000000000051": { "balance": "0" }, - "0000000000000000000000000000000000000052": { "balance": "0" }, - "0000000000000000000000000000000000000053": { "balance": "0" }, - "0000000000000000000000000000000000000054": { "balance": "0" }, - "0000000000000000000000000000000000000055": { "balance": "0" }, - "0000000000000000000000000000000000000056": { "balance": "0" }, - "0000000000000000000000000000000000000057": { "balance": "0" }, - "0000000000000000000000000000000000000058": { "balance": "0" }, - "0000000000000000000000000000000000000059": { "balance": "0" }, - "000000000000000000000000000000000000005a": { "balance": "0" }, - "000000000000000000000000000000000000005b": { "balance": "0" }, - "000000000000000000000000000000000000005c": { "balance": "0" }, - "000000000000000000000000000000000000005d": { "balance": "0" }, - "000000000000000000000000000000000000005e": { "balance": "0" }, - "000000000000000000000000000000000000005f": { "balance": "0" }, - "0000000000000000000000000000000000000060": { "balance": "0" }, - "0000000000000000000000000000000000000061": { "balance": "0" }, - "0000000000000000000000000000000000000062": { "balance": "0" }, - "0000000000000000000000000000000000000063": { "balance": "0" }, - "0000000000000000000000000000000000000064": { "balance": "0" }, - "0000000000000000000000000000000000000065": { "balance": "0" }, - "0000000000000000000000000000000000000066": { "balance": "0" }, - "0000000000000000000000000000000000000067": { "balance": "0" }, - "0000000000000000000000000000000000000068": { "balance": "0" }, - "0000000000000000000000000000000000000069": { "balance": "0" }, - "000000000000000000000000000000000000006a": { "balance": "0" }, - "000000000000000000000000000000000000006b": { "balance": "0" }, - "000000000000000000000000000000000000006c": { "balance": "0" }, - "000000000000000000000000000000000000006d": { "balance": "0" }, - "000000000000000000000000000000000000006e": { "balance": "0" }, - "000000000000000000000000000000000000006f": { "balance": "0" }, - "0000000000000000000000000000000000000070": { "balance": "0" }, - "0000000000000000000000000000000000000071": { "balance": "0" }, - "0000000000000000000000000000000000000072": { "balance": "0" }, - "0000000000000000000000000000000000000073": { "balance": "0" }, - "0000000000000000000000000000000000000074": { "balance": "0" }, - "0000000000000000000000000000000000000075": { "balance": "0" }, - "0000000000000000000000000000000000000076": { "balance": "0" }, - "0000000000000000000000000000000000000077": { "balance": "0" }, - "0000000000000000000000000000000000000078": { "balance": "0" }, - "0000000000000000000000000000000000000079": { "balance": "0" }, - "000000000000000000000000000000000000007a": { "balance": "0" }, - "000000000000000000000000000000000000007b": { "balance": "0" }, - "000000000000000000000000000000000000007c": { "balance": "0" }, - "000000000000000000000000000000000000007d": { "balance": "0" }, - "000000000000000000000000000000000000007e": { "balance": "0" }, - "000000000000000000000000000000000000007f": { "balance": "0" }, - "0000000000000000000000000000000000000080": { "balance": "0" }, - "0000000000000000000000000000000000000081": { "balance": "0" }, - "0000000000000000000000000000000000000082": { "balance": "0" }, - "0000000000000000000000000000000000000083": { "balance": "0" }, - "0000000000000000000000000000000000000084": { "balance": "0" }, - "0000000000000000000000000000000000000085": { "balance": "0" }, - "0000000000000000000000000000000000000086": { "balance": "0" }, - "0000000000000000000000000000000000000087": { "balance": "0" }, - "0000000000000000000000000000000000000088": { "balance": "0" }, - "0000000000000000000000000000000000000089": { "balance": "0" }, - "000000000000000000000000000000000000008a": { "balance": "0" }, - "000000000000000000000000000000000000008b": { "balance": "0" }, - "000000000000000000000000000000000000008c": { "balance": "0" }, - "000000000000000000000000000000000000008d": { "balance": "0" }, - "000000000000000000000000000000000000008e": { "balance": "0" }, - "000000000000000000000000000000000000008f": { "balance": "0" }, - "0000000000000000000000000000000000000090": { "balance": "0" }, - "0000000000000000000000000000000000000091": { "balance": "0" }, - "0000000000000000000000000000000000000092": { "balance": "0" }, - "0000000000000000000000000000000000000093": { "balance": "0" }, - "0000000000000000000000000000000000000094": { "balance": "0" }, - "0000000000000000000000000000000000000095": { "balance": "0" }, - "0000000000000000000000000000000000000096": { "balance": "0" }, - "0000000000000000000000000000000000000097": { "balance": "0" }, - "0000000000000000000000000000000000000098": { "balance": "0" }, - "0000000000000000000000000000000000000099": { "balance": "0" }, - "000000000000000000000000000000000000009a": { "balance": "0" }, - "000000000000000000000000000000000000009b": { "balance": "0" }, - "000000000000000000000000000000000000009c": { "balance": "0" }, - "000000000000000000000000000000000000009d": { "balance": "0" }, - "000000000000000000000000000000000000009e": { "balance": "0" }, - "000000000000000000000000000000000000009f": { "balance": "0" }, - "00000000000000000000000000000000000000a0": { "balance": "0" }, - "00000000000000000000000000000000000000a1": { "balance": "0" }, - "00000000000000000000000000000000000000a2": { "balance": "0" }, - "00000000000000000000000000000000000000a3": { "balance": "0" }, - "00000000000000000000000000000000000000a4": { "balance": "0" }, - "00000000000000000000000000000000000000a5": { "balance": "0" }, - "00000000000000000000000000000000000000a6": { "balance": "0" }, - "00000000000000000000000000000000000000a7": { "balance": "0" }, - "00000000000000000000000000000000000000a8": { "balance": "0" }, - "00000000000000000000000000000000000000a9": { "balance": "0" }, - "00000000000000000000000000000000000000aa": { "balance": "0" }, - "00000000000000000000000000000000000000ab": { "balance": "0" }, - "00000000000000000000000000000000000000ac": { "balance": "0" }, - "00000000000000000000000000000000000000ad": { "balance": "0" }, - "00000000000000000000000000000000000000ae": { "balance": "0" }, - "00000000000000000000000000000000000000af": { "balance": "0" }, - "00000000000000000000000000000000000000b0": { "balance": "0" }, - "00000000000000000000000000000000000000b1": { "balance": "0" }, - "00000000000000000000000000000000000000b2": { "balance": "0" }, - "00000000000000000000000000000000000000b3": { "balance": "0" }, - "00000000000000000000000000000000000000b4": { "balance": "0" }, - "00000000000000000000000000000000000000b5": { "balance": "0" }, - "00000000000000000000000000000000000000b6": { "balance": "0" }, - "00000000000000000000000000000000000000b7": { "balance": "0" }, - "00000000000000000000000000000000000000b8": { "balance": "0" }, - "00000000000000000000000000000000000000b9": { "balance": "0" }, - "00000000000000000000000000000000000000ba": { "balance": "0" }, - "00000000000000000000000000000000000000bb": { "balance": "0" }, - "00000000000000000000000000000000000000bc": { "balance": "0" }, - "00000000000000000000000000000000000000bd": { "balance": "0" }, - "00000000000000000000000000000000000000be": { "balance": "0" }, - "00000000000000000000000000000000000000bf": { "balance": "0" }, - "00000000000000000000000000000000000000c0": { "balance": "0" }, - "00000000000000000000000000000000000000c1": { "balance": "0" }, - "00000000000000000000000000000000000000c2": { "balance": "0" }, - "00000000000000000000000000000000000000c3": { "balance": "0" }, - "00000000000000000000000000000000000000c4": { "balance": "0" }, - "00000000000000000000000000000000000000c5": { "balance": "0" }, - "00000000000000000000000000000000000000c6": { "balance": "0" }, - "00000000000000000000000000000000000000c7": { "balance": "0" }, - "00000000000000000000000000000000000000c8": { "balance": "0" }, - "00000000000000000000000000000000000000c9": { "balance": "0" }, - "00000000000000000000000000000000000000ca": { "balance": "0" }, - "00000000000000000000000000000000000000cb": { "balance": "0" }, - "00000000000000000000000000000000000000cc": { "balance": "0" }, - "00000000000000000000000000000000000000cd": { "balance": "0" }, - "00000000000000000000000000000000000000ce": { "balance": "0" }, - "00000000000000000000000000000000000000cf": { "balance": "0" }, - "00000000000000000000000000000000000000d0": { "balance": "0" }, - "00000000000000000000000000000000000000d1": { "balance": "0" }, - "00000000000000000000000000000000000000d2": { "balance": "0" }, - "00000000000000000000000000000000000000d3": { "balance": "0" }, - "00000000000000000000000000000000000000d4": { "balance": "0" }, - "00000000000000000000000000000000000000d5": { "balance": "0" }, - "00000000000000000000000000000000000000d6": { "balance": "0" }, - "00000000000000000000000000000000000000d7": { "balance": "0" }, - "00000000000000000000000000000000000000d8": { "balance": "0" }, - "00000000000000000000000000000000000000d9": { "balance": "0" }, + "0000000000000000000000000000000000000000": { "balance": "1" }, + "0000000000000000000000000000000000000005": { "balance": "1" }, + "0000000000000000000000000000000000000006": { "balance": "1" }, + "0000000000000000000000000000000000000007": { "balance": "1" }, + "0000000000000000000000000000000000000008": { "balance": "1" }, + "0000000000000000000000000000000000000009": { "balance": "1" }, + "000000000000000000000000000000000000000a": { "balance": "0" }, + "000000000000000000000000000000000000000b": { "balance": "0" }, + "000000000000000000000000000000000000000c": { "balance": "0" }, + "000000000000000000000000000000000000000d": { "balance": "0" }, + "000000000000000000000000000000000000000e": { "balance": "0" }, + "000000000000000000000000000000000000000f": { "balance": "0" }, + "0000000000000000000000000000000000000010": { "balance": "0" }, + "0000000000000000000000000000000000000011": { "balance": "0" }, + "0000000000000000000000000000000000000012": { "balance": "0" }, + "0000000000000000000000000000000000000013": { "balance": "0" }, + "0000000000000000000000000000000000000014": { "balance": "0" }, + "0000000000000000000000000000000000000015": { "balance": "0" }, + "0000000000000000000000000000000000000016": { "balance": "0" }, + "0000000000000000000000000000000000000017": { "balance": "0" }, + "0000000000000000000000000000000000000018": { "balance": "0" }, + "0000000000000000000000000000000000000019": { "balance": "0" }, + "000000000000000000000000000000000000001a": { "balance": "0" }, + "000000000000000000000000000000000000001b": { "balance": "0" }, + "000000000000000000000000000000000000001c": { "balance": "0" }, + "000000000000000000000000000000000000001d": { "balance": "0" }, + "000000000000000000000000000000000000001e": { "balance": "0" }, + "000000000000000000000000000000000000001f": { "balance": "0" }, + "0000000000000000000000000000000000000020": { "balance": "0" }, + "0000000000000000000000000000000000000021": { "balance": "0" }, + "0000000000000000000000000000000000000022": { "balance": "0" }, + "0000000000000000000000000000000000000023": { "balance": "0" }, + "0000000000000000000000000000000000000024": { "balance": "0" }, + "0000000000000000000000000000000000000025": { "balance": "0" }, + "0000000000000000000000000000000000000026": { "balance": "0" }, + "0000000000000000000000000000000000000027": { "balance": "0" }, + "0000000000000000000000000000000000000028": { "balance": "0" }, + "0000000000000000000000000000000000000029": { "balance": "0" }, + "000000000000000000000000000000000000002a": { "balance": "0" }, + "000000000000000000000000000000000000002b": { "balance": "0" }, + "000000000000000000000000000000000000002c": { "balance": "0" }, + "000000000000000000000000000000000000002d": { "balance": "0" }, + "000000000000000000000000000000000000002e": { "balance": "0" }, + "000000000000000000000000000000000000002f": { "balance": "0" }, + "0000000000000000000000000000000000000030": { "balance": "0" }, + "0000000000000000000000000000000000000031": { "balance": "0" }, + "0000000000000000000000000000000000000032": { "balance": "0" }, + "0000000000000000000000000000000000000033": { "balance": "0" }, + "0000000000000000000000000000000000000034": { "balance": "0" }, + "0000000000000000000000000000000000000035": { "balance": "0" }, + "0000000000000000000000000000000000000036": { "balance": "0" }, + "0000000000000000000000000000000000000037": { "balance": "0" }, + "0000000000000000000000000000000000000038": { "balance": "0" }, + "0000000000000000000000000000000000000039": { "balance": "0" }, + "000000000000000000000000000000000000003a": { "balance": "0" }, + "000000000000000000000000000000000000003b": { "balance": "0" }, + "000000000000000000000000000000000000003c": { "balance": "0" }, + "000000000000000000000000000000000000003d": { "balance": "0" }, + "000000000000000000000000000000000000003e": { "balance": "0" }, + "000000000000000000000000000000000000003f": { "balance": "0" }, + "0000000000000000000000000000000000000040": { "balance": "0" }, + "0000000000000000000000000000000000000041": { "balance": "0" }, + "0000000000000000000000000000000000000042": { "balance": "0" }, + "0000000000000000000000000000000000000043": { "balance": "0" }, + "0000000000000000000000000000000000000044": { "balance": "0" }, + "0000000000000000000000000000000000000045": { "balance": "0" }, + "0000000000000000000000000000000000000046": { "balance": "0" }, + "0000000000000000000000000000000000000047": { "balance": "0" }, + "0000000000000000000000000000000000000048": { "balance": "0" }, + "0000000000000000000000000000000000000049": { "balance": "0" }, + "000000000000000000000000000000000000004a": { "balance": "0" }, + "000000000000000000000000000000000000004b": { "balance": "0" }, + "000000000000000000000000000000000000004c": { "balance": "0" }, + "000000000000000000000000000000000000004d": { "balance": "0" }, + "000000000000000000000000000000000000004e": { "balance": "0" }, + "000000000000000000000000000000000000004f": { "balance": "0" }, + "0000000000000000000000000000000000000050": { "balance": "0" }, + "0000000000000000000000000000000000000051": { "balance": "0" }, + "0000000000000000000000000000000000000052": { "balance": "0" }, + "0000000000000000000000000000000000000053": { "balance": "0" }, + "0000000000000000000000000000000000000054": { "balance": "0" }, + "0000000000000000000000000000000000000055": { "balance": "0" }, + "0000000000000000000000000000000000000056": { "balance": "0" }, + "0000000000000000000000000000000000000057": { "balance": "0" }, + "0000000000000000000000000000000000000058": { "balance": "0" }, + "0000000000000000000000000000000000000059": { "balance": "0" }, + "000000000000000000000000000000000000005a": { "balance": "0" }, + "000000000000000000000000000000000000005b": { "balance": "0" }, + "000000000000000000000000000000000000005c": { "balance": "0" }, + "000000000000000000000000000000000000005d": { "balance": "0" }, + "000000000000000000000000000000000000005e": { "balance": "0" }, + "000000000000000000000000000000000000005f": { "balance": "0" }, + "0000000000000000000000000000000000000060": { "balance": "0" }, + "0000000000000000000000000000000000000061": { "balance": "0" }, + "0000000000000000000000000000000000000062": { "balance": "0" }, + "0000000000000000000000000000000000000063": { "balance": "0" }, + "0000000000000000000000000000000000000064": { "balance": "0" }, + "0000000000000000000000000000000000000065": { "balance": "0" }, + "0000000000000000000000000000000000000066": { "balance": "0" }, + "0000000000000000000000000000000000000067": { "balance": "0" }, + "0000000000000000000000000000000000000068": { "balance": "0" }, + "0000000000000000000000000000000000000069": { "balance": "0" }, + "000000000000000000000000000000000000006a": { "balance": "0" }, + "000000000000000000000000000000000000006b": { "balance": "0" }, + "000000000000000000000000000000000000006c": { "balance": "0" }, + "000000000000000000000000000000000000006d": { "balance": "0" }, + "000000000000000000000000000000000000006e": { "balance": "0" }, + "000000000000000000000000000000000000006f": { "balance": "0" }, + "0000000000000000000000000000000000000070": { "balance": "0" }, + "0000000000000000000000000000000000000071": { "balance": "0" }, + "0000000000000000000000000000000000000072": { "balance": "0" }, + "0000000000000000000000000000000000000073": { "balance": "0" }, + "0000000000000000000000000000000000000074": { "balance": "0" }, + "0000000000000000000000000000000000000075": { "balance": "0" }, + "0000000000000000000000000000000000000076": { "balance": "0" }, + "0000000000000000000000000000000000000077": { "balance": "0" }, + "0000000000000000000000000000000000000078": { "balance": "0" }, + "0000000000000000000000000000000000000079": { "balance": "0" }, + "000000000000000000000000000000000000007a": { "balance": "0" }, + "000000000000000000000000000000000000007b": { "balance": "0" }, + "000000000000000000000000000000000000007c": { "balance": "0" }, + "000000000000000000000000000000000000007d": { "balance": "0" }, + "000000000000000000000000000000000000007e": { "balance": "0" }, + "000000000000000000000000000000000000007f": { "balance": "0" }, + "0000000000000000000000000000000000000080": { "balance": "0" }, + "0000000000000000000000000000000000000081": { "balance": "0" }, + "0000000000000000000000000000000000000082": { "balance": "0" }, + "0000000000000000000000000000000000000083": { "balance": "0" }, + "0000000000000000000000000000000000000084": { "balance": "0" }, + "0000000000000000000000000000000000000085": { "balance": "0" }, + "0000000000000000000000000000000000000086": { "balance": "0" }, + "0000000000000000000000000000000000000087": { "balance": "0" }, + "0000000000000000000000000000000000000088": { "balance": "0" }, + "0000000000000000000000000000000000000089": { "balance": "0" }, + "000000000000000000000000000000000000008a": { "balance": "0" }, + "000000000000000000000000000000000000008b": { "balance": "0" }, + "000000000000000000000000000000000000008c": { "balance": "0" }, + "000000000000000000000000000000000000008d": { "balance": "0" }, + "000000000000000000000000000000000000008e": { "balance": "0" }, + "000000000000000000000000000000000000008f": { "balance": "0" }, + "0000000000000000000000000000000000000090": { "balance": "0" }, + "0000000000000000000000000000000000000091": { "balance": "0" }, + "0000000000000000000000000000000000000092": { "balance": "0" }, + "0000000000000000000000000000000000000093": { "balance": "0" }, + "0000000000000000000000000000000000000094": { "balance": "0" }, + "0000000000000000000000000000000000000095": { "balance": "0" }, + "0000000000000000000000000000000000000096": { "balance": "0" }, + "0000000000000000000000000000000000000097": { "balance": "0" }, + "0000000000000000000000000000000000000098": { "balance": "0" }, + "0000000000000000000000000000000000000099": { "balance": "0" }, + "000000000000000000000000000000000000009a": { "balance": "0" }, + "000000000000000000000000000000000000009b": { "balance": "0" }, + "000000000000000000000000000000000000009c": { "balance": "0" }, + "000000000000000000000000000000000000009d": { "balance": "0" }, + "000000000000000000000000000000000000009e": { "balance": "0" }, + "000000000000000000000000000000000000009f": { "balance": "0" }, + "00000000000000000000000000000000000000a0": { "balance": "0" }, + "00000000000000000000000000000000000000a1": { "balance": "0" }, + "00000000000000000000000000000000000000a2": { "balance": "0" }, + "00000000000000000000000000000000000000a3": { "balance": "0" }, + "00000000000000000000000000000000000000a4": { "balance": "0" }, + "00000000000000000000000000000000000000a5": { "balance": "0" }, + "00000000000000000000000000000000000000a6": { "balance": "0" }, + "00000000000000000000000000000000000000a7": { "balance": "0" }, + "00000000000000000000000000000000000000a8": { "balance": "0" }, + "00000000000000000000000000000000000000a9": { "balance": "0" }, + "00000000000000000000000000000000000000aa": { "balance": "0" }, + "00000000000000000000000000000000000000ab": { "balance": "0" }, + "00000000000000000000000000000000000000ac": { "balance": "0" }, + "00000000000000000000000000000000000000ad": { "balance": "0" }, + "00000000000000000000000000000000000000ae": { "balance": "0" }, + "00000000000000000000000000000000000000af": { "balance": "0" }, + "00000000000000000000000000000000000000b0": { "balance": "0" }, + "00000000000000000000000000000000000000b1": { "balance": "0" }, + "00000000000000000000000000000000000000b2": { "balance": "0" }, + "00000000000000000000000000000000000000b3": { "balance": "0" }, + "00000000000000000000000000000000000000b4": { "balance": "0" }, + "00000000000000000000000000000000000000b5": { "balance": "0" }, + "00000000000000000000000000000000000000b6": { "balance": "0" }, + "00000000000000000000000000000000000000b7": { "balance": "0" }, + "00000000000000000000000000000000000000b8": { "balance": "0" }, + "00000000000000000000000000000000000000b9": { "balance": "0" }, + "00000000000000000000000000000000000000ba": { "balance": "0" }, + "00000000000000000000000000000000000000bb": { "balance": "0" }, + "00000000000000000000000000000000000000bc": { "balance": "0" }, + "00000000000000000000000000000000000000bd": { "balance": "0" }, + "00000000000000000000000000000000000000be": { "balance": "0" }, + "00000000000000000000000000000000000000bf": { "balance": "0" }, + "00000000000000000000000000000000000000c0": { "balance": "0" }, + "00000000000000000000000000000000000000c1": { "balance": "0" }, + "00000000000000000000000000000000000000c2": { "balance": "0" }, + "00000000000000000000000000000000000000c3": { "balance": "0" }, + "00000000000000000000000000000000000000c4": { "balance": "0" }, + "00000000000000000000000000000000000000c5": { "balance": "0" }, + "00000000000000000000000000000000000000c6": { "balance": "0" }, + "00000000000000000000000000000000000000c7": { "balance": "0" }, + "00000000000000000000000000000000000000c8": { "balance": "0" }, + "00000000000000000000000000000000000000c9": { "balance": "0" }, + "00000000000000000000000000000000000000ca": { "balance": "0" }, + "00000000000000000000000000000000000000cb": { "balance": "0" }, + "00000000000000000000000000000000000000cc": { "balance": "0" }, + "00000000000000000000000000000000000000cd": { "balance": "0" }, + "00000000000000000000000000000000000000ce": { "balance": "0" }, + "00000000000000000000000000000000000000cf": { "balance": "0" }, + "00000000000000000000000000000000000000d0": { "balance": "0" }, + "00000000000000000000000000000000000000d1": { "balance": "0" }, + "00000000000000000000000000000000000000d2": { "balance": "0" }, + "00000000000000000000000000000000000000d3": { "balance": "0" }, + "00000000000000000000000000000000000000d4": { "balance": "0" }, + "00000000000000000000000000000000000000d5": { "balance": "0" }, + "00000000000000000000000000000000000000d6": { "balance": "0" }, + "00000000000000000000000000000000000000d7": { "balance": "0" }, + "00000000000000000000000000000000000000d8": { "balance": "0" }, + "00000000000000000000000000000000000000d9": { "balance": "0" }, "00000000000000000000000000000000000000da": { "balance": "0" }, - "00000000000000000000000000000000000000db": { "balance": "0" }, - "00000000000000000000000000000000000000dc": { "balance": "0" }, - "00000000000000000000000000000000000000dd": { "balance": "0" }, - "00000000000000000000000000000000000000de": { "balance": "0" }, - "00000000000000000000000000000000000000df": { "balance": "0" }, - "00000000000000000000000000000000000000e0": { "balance": "0" }, - "00000000000000000000000000000000000000e1": { "balance": "0" }, - "00000000000000000000000000000000000000e2": { "balance": "0" }, - "00000000000000000000000000000000000000e3": { "balance": "0" }, - "00000000000000000000000000000000000000e4": { "balance": "0" }, - "00000000000000000000000000000000000000e5": { "balance": "0" }, - "00000000000000000000000000000000000000e6": { "balance": "0" }, - "00000000000000000000000000000000000000e7": { "balance": "0" }, - "00000000000000000000000000000000000000e8": { "balance": "0" }, - "00000000000000000000000000000000000000e9": { "balance": "0" }, - "00000000000000000000000000000000000000ea": { "balance": "0" }, - "00000000000000000000000000000000000000eb": { "balance": "0" }, - "00000000000000000000000000000000000000ec": { "balance": "0" }, - "00000000000000000000000000000000000000ed": { "balance": "0" }, - "00000000000000000000000000000000000000ee": { "balance": "0" }, - "00000000000000000000000000000000000000ef": { "balance": "0" }, - "00000000000000000000000000000000000000f0": { "balance": "0" }, - "00000000000000000000000000000000000000f1": { "balance": "0" }, - "00000000000000000000000000000000000000f2": { "balance": "0" }, - "00000000000000000000000000000000000000f3": { "balance": "0" }, - "00000000000000000000000000000000000000f4": { "balance": "0" }, - "00000000000000000000000000000000000000f5": { "balance": "0" }, - "00000000000000000000000000000000000000f6": { "balance": "0" }, - "00000000000000000000000000000000000000f7": { "balance": "0" }, - "00000000000000000000000000000000000000f8": { "balance": "0" }, - "00000000000000000000000000000000000000f9": { "balance": "0" }, - "00000000000000000000000000000000000000fa": { "balance": "0" }, - "00000000000000000000000000000000000000fb": { "balance": "0" }, - "00000000000000000000000000000000000000fc": { "balance": "0" }, - "00000000000000000000000000000000000000fd": { "balance": "0" }, - "00000000000000000000000000000000000000fe": { "balance": "0" }, - "00000000000000000000000000000000000000ff": { "balance": "0" }, + "00000000000000000000000000000000000000db": { "balance": "0" }, + "00000000000000000000000000000000000000dc": { "balance": "0" }, + "00000000000000000000000000000000000000dd": { "balance": "0" }, + "00000000000000000000000000000000000000de": { "balance": "0" }, + "00000000000000000000000000000000000000df": { "balance": "0" }, + "00000000000000000000000000000000000000e0": { "balance": "0" }, + "00000000000000000000000000000000000000e1": { "balance": "0" }, + "00000000000000000000000000000000000000e2": { "balance": "0" }, + "00000000000000000000000000000000000000e3": { "balance": "0" }, + "00000000000000000000000000000000000000e4": { "balance": "0" }, + "00000000000000000000000000000000000000e5": { "balance": "0" }, + "00000000000000000000000000000000000000e6": { "balance": "0" }, + "00000000000000000000000000000000000000e7": { "balance": "0" }, + "00000000000000000000000000000000000000e8": { "balance": "0" }, + "00000000000000000000000000000000000000e9": { "balance": "0" }, + "00000000000000000000000000000000000000ea": { "balance": "0" }, + "00000000000000000000000000000000000000eb": { "balance": "0" }, + "00000000000000000000000000000000000000ec": { "balance": "0" }, + "00000000000000000000000000000000000000ed": { "balance": "0" }, + "00000000000000000000000000000000000000ee": { "balance": "0" }, + "00000000000000000000000000000000000000ef": { "balance": "0" }, + "00000000000000000000000000000000000000f0": { "balance": "0" }, + "00000000000000000000000000000000000000f1": { "balance": "0" }, + "00000000000000000000000000000000000000f2": { "balance": "0" }, + "00000000000000000000000000000000000000f3": { "balance": "0" }, + "00000000000000000000000000000000000000f4": { "balance": "0" }, + "00000000000000000000000000000000000000f5": { "balance": "0" }, + "00000000000000000000000000000000000000f6": { "balance": "0" }, + "00000000000000000000000000000000000000f7": { "balance": "0" }, + "00000000000000000000000000000000000000f8": { "balance": "0" }, + "00000000000000000000000000000000000000f9": { "balance": "0" }, + "00000000000000000000000000000000000000fa": { "balance": "0" }, + "00000000000000000000000000000000000000fb": { "balance": "0" }, + "00000000000000000000000000000000000000fc": { "balance": "0" }, + "00000000000000000000000000000000000000fd": { "balance": "0" }, + "00000000000000000000000000000000000000fe": { "balance": "0" }, + "00000000000000000000000000000000000000ff": { "balance": "0" }, "874b54a8bd152966d63f706bae1ffeb0411921e5": { "balance": "1000000000000000000000000000000" } } } From 65785475f35a1978e8da8182d8b12a8d7cb4867b Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Mon, 21 Nov 2016 17:37:37 +0000 Subject: [PATCH 35/97] [ci skip] js-precompiled 20161121-173542 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 02e45b49e..a9af4e0c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1263,7 +1263,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#587684374a12bf715151dd987a552a3d61e42972" +source = "git+https://github.com/ethcore/js-precompiled.git#253103aef1579e6ff11d5efe8b31f394fd0857bc" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index 7f4157cad..e4d34c316 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.58", + "version": "0.2.59", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From eb94eda30b680467425ab5e088ab8c3949b081a3 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Mon, 21 Nov 2016 18:16:10 +0000 Subject: [PATCH 36/97] [ci skip] js-precompiled 20161121-181249 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a9af4e0c6..dd05fc18d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1263,7 +1263,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#253103aef1579e6ff11d5efe8b31f394fd0857bc" +source = "git+https://github.com/ethcore/js-precompiled.git#a0cf68024199f7a8c39c7d149ad622ac2f72129a" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index e4d34c316..bed226f02 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.59", + "version": "0.2.60", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From 8a1b585da23f8b250cb6f2019ff2da49714f6b84 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Mon, 21 Nov 2016 20:44:07 +0100 Subject: [PATCH 37/97] Fix peers not displaying (#3561) * Add peers count to status polling * Array destruct for better readability --- js/src/redux/providers/status.js | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/js/src/redux/providers/status.js b/js/src/redux/providers/status.js index bdc4dd38b..50b28a5ac 100644 --- a/js/src/redux/providers/status.js +++ b/js/src/redux/providers/status.js @@ -155,24 +155,20 @@ export default class Status { const { refreshStatus } = this._store.getState().nodeStatus; - const statusPromises = [ this._api.eth.syncing() ]; + const statusPromises = [ this._api.eth.syncing(), this._api.parity.netPeers() ]; if (refreshStatus) { statusPromises.push(this._api.eth.hashrate()); - statusPromises.push(this._api.parity.netPeers()); } Promise .all(statusPromises) - .then((statusResults) => { - const status = statusResults.length === 1 - ? { - syncing: statusResults[0] - } + .then(([ syncing, netPeers, ...statusResults ]) => { + const status = statusResults.length === 0 + ? { syncing, netPeers } : { - syncing: statusResults[0], - hashrate: statusResults[1], - netPeers: statusResults[2] + syncing, netPeers, + hashrate: statusResults[0] }; if (!isEqual(status, this._status)) { From 5735d948f5fd327f71d851bdf2d8f29061b2e098 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Mon, 21 Nov 2016 20:44:37 +0100 Subject: [PATCH 38/97] [Registry] Clear input and working buttons (#3563) * onClick to onTouchTap #3556 * onClick to onTouchTap 2 #3556 * Registry dApp clear input + check Signer #3557 --- js/src/dapps/registry/Lookup/lookup.js | 2 +- js/src/dapps/registry/Names/actions.js | 27 +++++++++++++++---- js/src/dapps/registry/Names/names.js | 18 ++++++++++++- js/src/dapps/registry/Records/records.js | 2 +- js/src/modals/LoadContract/loadContract.js | 2 +- js/src/ui/Form/TypedInput/typedInput.js | 4 +-- js/src/views/Dapps/dapps.js | 2 +- .../TransactionPendingFormConfirm.js | 2 +- .../TransactionPendingFormReject.js | 2 +- .../components/CallsToolbar/CallsToolbar.js | 4 +-- .../ScrollTopButton/ScrollTopButton.js | 2 +- 11 files changed, 50 insertions(+), 17 deletions(-) diff --git a/js/src/dapps/registry/Lookup/lookup.js b/js/src/dapps/registry/Lookup/lookup.js index 4238f1160..436d113b9 100644 --- a/js/src/dapps/registry/Lookup/lookup.js +++ b/js/src/dapps/registry/Lookup/lookup.js @@ -77,7 +77,7 @@ export default class Lookup extends Component { label='Lookup' primary icon={ } - onClick={ this.onLookupClick } + onTouchTap={ this.onLookupClick } />
{ output } diff --git a/js/src/dapps/registry/Names/actions.js b/js/src/dapps/registry/Names/actions.js index 74e446d9d..488145331 100644 --- a/js/src/dapps/registry/Names/actions.js +++ b/js/src/dapps/registry/Names/actions.js @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import { sha3 } from '../parity.js'; +import { sha3, api } from '../parity.js'; const alreadyQueued = (queue, action, name) => !!queue.find((entry) => entry.action === action && entry.name === name); @@ -43,14 +43,23 @@ export const reserve = (name) => (dispatch, getState) => { const values = [ sha3(name) ]; dispatch(reserveStart(name)); + reserve.estimateGas(options, values) .then((gas) => { options.gas = gas.mul(1.2).toFixed(0); return reserve.postTransaction(options, values); }) - .then((data) => { + .then((requestId) => { + return api.pollMethod('parity_checkRequest', requestId); + }) + .then((txhash) => { dispatch(reserveSuccess(name)); - }).catch((err) => { + }) + .catch((err) => { + if (err && err.type === 'REQUEST_REJECTED') { + return dispatch(reserveFail(name)); + } + console.error(`could not reserve ${name}`); if (err) console.error(err.stack); dispatch(reserveFail(name)); @@ -81,9 +90,17 @@ export const drop = (name) => (dispatch, getState) => { options.gas = gas.mul(1.2).toFixed(0); return drop.postTransaction(options, values); }) - .then((data) => { + .then((requestId) => { + return api.pollMethod('parity_checkRequest', requestId); + }) + .then((txhash) => { dispatch(dropSuccess(name)); - }).catch((err) => { + }) + .catch((err) => { + if (err && err.type === 'REQUEST_REJECTED') { + dispatch(reserveFail(name)); + } + console.error(`could not drop ${name}`); if (err) console.error(err.stack); dispatch(reserveFail(name)); diff --git a/js/src/dapps/registry/Names/names.js b/js/src/dapps/registry/Names/names.js index dd7e6f772..369d9690c 100644 --- a/js/src/dapps/registry/Names/names.js +++ b/js/src/dapps/registry/Names/names.js @@ -86,6 +86,22 @@ export default class Names extends Component { name: '' }; + componentWillReceiveProps (nextProps) { + const nextQueue = nextProps.queue; + const prevQueue = this.props.queue; + + if (nextQueue.length > prevQueue.length) { + const newQueued = nextQueue[nextQueue.length - 1]; + const newName = newQueued.name; + + if (newName !== this.state.name) { + return; + } + + this.setState({ name: '' }); + } + } + render () { const { action, name } = this.state; const { fee, pending, queue } = this.props; @@ -120,7 +136,7 @@ export default class Names extends Component { label={ action === 'reserve' ? 'Reserve' : 'Drop' } primary icon={ } - onClick={ this.onSubmitClick } + onTouchTap={ this.onSubmitClick } /> { queue.length > 0 ? (
{ useSignerText }{ renderQueue(queue) }
) diff --git a/js/src/dapps/registry/Records/records.js b/js/src/dapps/registry/Records/records.js index 60640893c..89c751c36 100644 --- a/js/src/dapps/registry/Records/records.js +++ b/js/src/dapps/registry/Records/records.js @@ -52,7 +52,7 @@ export default class Records extends Component { label='Save' primary icon={ } - onClick={ this.onSaveClick } + onTouchTap={ this.onSaveClick } /> diff --git a/js/src/modals/LoadContract/loadContract.js b/js/src/modals/LoadContract/loadContract.js index 3de55561a..f67a2dce6 100644 --- a/js/src/modals/LoadContract/loadContract.js +++ b/js/src/modals/LoadContract/loadContract.js @@ -174,7 +174,7 @@ export default class LoadContract extends Component { const secondaryText = description || `Saved ${moment(timestamp).fromNow()}`; const remove = removable ? ( - + ) diff --git a/js/src/ui/Form/TypedInput/typedInput.js b/js/src/ui/Form/TypedInput/typedInput.js index 6bcb5bbf1..f5ec2a02f 100644 --- a/js/src/ui/Form/TypedInput/typedInput.js +++ b/js/src/ui/Form/TypedInput/typedInput.js @@ -96,7 +96,7 @@ export default class TypedInput extends Component { @@ -104,7 +104,7 @@ export default class TypedInput extends Component { diff --git a/js/src/views/Dapps/dapps.js b/js/src/views/Dapps/dapps.js index 5d87b808d..7faead576 100644 --- a/js/src/views/Dapps/dapps.js +++ b/js/src/views/Dapps/dapps.js @@ -67,7 +67,7 @@ export default class Dapps extends Component { label='edit' key='edit' icon={ } - onClick={ this.store.openModal } + onTouchTap={ this.store.openModal } /> ] } /> diff --git a/js/src/views/Signer/components/TransactionPendingForm/TransactionPendingFormConfirm/TransactionPendingFormConfirm.js b/js/src/views/Signer/components/TransactionPendingForm/TransactionPendingFormConfirm/TransactionPendingFormConfirm.js index 5765447ee..f2cc0f910 100644 --- a/js/src/views/Signer/components/TransactionPendingForm/TransactionPendingFormConfirm/TransactionPendingFormConfirm.js +++ b/js/src/views/Signer/components/TransactionPendingForm/TransactionPendingFormConfirm/TransactionPendingFormConfirm.js @@ -74,7 +74,7 @@ class TransactionPendingFormConfirm extends Component { data-effect='solid' > This cannot be undone
+ onTouchTap={ this._scrollToTop }> ); From f9ecea8f4de9d27eb3e3f19e82f95bba4be17746 Mon Sep 17 00:00:00 2001 From: Jannis Redmann Date: Mon, 21 Nov 2016 20:45:47 +0100 Subject: [PATCH 39/97] sms verification code style (#3564) * sms verification: fix code style * sms verification: move server-related code to 3rdparty * sms verification: adapt to ropsten --- .../sms-verification/index.js} | 18 ++++- js/src/contracts/sms-verification.js | 17 ---- .../SMSVerification/GatherData/gatherData.js | 7 +- .../modals/SMSVerification/SMSVerification.js | 80 +++++++++++-------- js/src/modals/SMSVerification/store.js | 5 +- 5 files changed, 68 insertions(+), 59 deletions(-) rename js/src/{modals/SMSVerification/terms-of-service.js => 3rdparty/sms-verification/index.js} (81%) diff --git a/js/src/modals/SMSVerification/terms-of-service.js b/js/src/3rdparty/sms-verification/index.js similarity index 81% rename from js/src/modals/SMSVerification/terms-of-service.js rename to js/src/3rdparty/sms-verification/index.js index f61b3c97d..9b113f364 100644 --- a/js/src/modals/SMSVerification/terms-of-service.js +++ b/js/src/3rdparty/sms-verification/index.js @@ -14,9 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +import { stringify } from 'querystring'; import React from 'react'; -export default ( +export const termsOfService = (
  • This privacy notice relates to your use of the Parity SMS verification service. We take your privacy seriously and deal in an honest, direct and transparent way when it comes to your data.
  • We collect your phone number when you use this service. This is temporarily kept in memory, and then encrypted and stored in our EU servers. We only retain the cryptographic hash of the number to prevent duplicated accounts. You consent to this use.
  • @@ -25,3 +26,18 @@ export default (
  • Parity Technology Limited is registered in England and Wales under company number 09760015 and complies with the Data Protection Act 1998 (UK). You may contact us via email at admin@parity.io. Our general privacy policy can be found here: https://ethcore.io/legal.html.
); + +export const postToServer = (query) => { + query = stringify(query); + return fetch('https://sms-verification.parity.io/?' + query, { + method: 'POST', mode: 'cors', cache: 'no-store' + }) + .then((res) => { + return res.json().then((data) => { + if (res.ok) { + return data.message; + } + throw new Error(data.message || 'unknown error'); + }); + }); +}; diff --git a/js/src/contracts/sms-verification.js b/js/src/contracts/sms-verification.js index e93d57ffc..c6893e639 100644 --- a/js/src/contracts/sms-verification.js +++ b/js/src/contracts/sms-verification.js @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import { stringify } from 'querystring'; - export const checkIfVerified = (contract, account) => { return contract.instance.certified.call({}, [account]); }; @@ -35,18 +33,3 @@ export const checkIfRequested = (contract, account) => { }); }); }; - -export const postToServer = (query) => { - query = stringify(query); - return fetch('https://sms-verification.parity.io/?' + query, { - method: 'POST', mode: 'cors', cache: 'no-store' - }) - .then((res) => { - return res.json().then((data) => { - if (res.ok) { - return data.message; - } - throw new Error(data.message || 'unknown error'); - }); - }); -}; diff --git a/js/src/modals/SMSVerification/GatherData/gatherData.js b/js/src/modals/SMSVerification/GatherData/gatherData.js index f4036a3bc..3620de904 100644 --- a/js/src/modals/SMSVerification/GatherData/gatherData.js +++ b/js/src/modals/SMSVerification/GatherData/gatherData.js @@ -25,7 +25,7 @@ import ErrorIcon from 'material-ui/svg-icons/navigation/close'; import { fromWei } from '../../../api/util/wei'; import { Form, Input } from '../../../ui'; -import terms from '../terms-of-service'; +import { termsOfService } from '../../../3rdparty/sms-verification'; import styles from './gatherData.css'; export default class GatherData extends Component { @@ -66,7 +66,7 @@ export default class GatherData extends Component { disabled={ isVerified } onCheck={ this.consentOnChange } /> -
{ terms }
+
{ termsOfService }
); } @@ -123,8 +123,7 @@ export default class GatherData extends Component {

You already requested verification.

); - } - if (hasRequested === false) { + } else if (hasRequested === false) { return (
diff --git a/js/src/modals/SMSVerification/SMSVerification.js b/js/src/modals/SMSVerification/SMSVerification.js index 4ec0b608d..b7c8a901a 100644 --- a/js/src/modals/SMSVerification/SMSVerification.js +++ b/js/src/modals/SMSVerification/SMSVerification.js @@ -16,8 +16,8 @@ import React, { Component, PropTypes } from 'react'; import { observer } from 'mobx-react'; -import ActionDoneAll from 'material-ui/svg-icons/action/done-all'; -import ContentClear from 'material-ui/svg-icons/content/clear'; +import DoneIcon from 'material-ui/svg-icons/action/done-all'; +import CancelIcon from 'material-ui/svg-icons/content/clear'; import { Button, IdentityIcon, Modal } from '../../ui'; @@ -77,7 +77,7 @@ export default class SMSVerification extends Component { const cancel = (
@@ -140,37 +140,47 @@ export default class SMSVerification extends Component { setNumber, setConsentGiven, setCode } = this.props.store; - if (phase === 5) { - return (); - } - if (phase === 4) { - return (); - } - if (phase === 3) { - return ( - - ); - } - if (phase === 2) { - return (); - } - if (phase === 1) { - const { setNumber, setConsentGiven } = this.props.store; - return ( - - ); - } - if (phase === 0) { - return (

Preparing awesomeness!

); - } + switch (phase) { + case 0: + return ( +

Loading SMS Verification.

+ ); - return null; + case 1: + const { setNumber, setConsentGiven } = this.props.store; + return ( + + ); + + case 2: + return ( + + ); + + case 3: + return ( + + ); + + case 4: + return ( + + ); + + case 5: + return ( + + ); + + default: + return null; + } } } diff --git a/js/src/modals/SMSVerification/store.js b/js/src/modals/SMSVerification/store.js index 7337f4eac..8c4db373a 100644 --- a/js/src/modals/SMSVerification/store.js +++ b/js/src/modals/SMSVerification/store.js @@ -20,7 +20,8 @@ import { sha3 } from '../../api/util/sha3'; import Contracts from '../../contracts'; -import { checkIfVerified, checkIfRequested, postToServer } from '../../contracts/sms-verification'; +import { checkIfVerified, checkIfRequested } from '../../contracts/sms-verification'; +import { postToServer } from '../../3rdparty/sms-verification'; import checkIfTxFailed from '../../util/check-if-tx-failed'; import waitForConfirmations from '../../util/wait-for-block-confirmations'; @@ -87,7 +88,7 @@ export default class VerificationStore { this.account = account; this.step = LOADING; - Contracts.create(api).registry.getContract('smsVerification') + Contracts.create(api).registry.getContract('smsverification') .then((contract) => { this.contract = contract; this.load(); From b97763e13de1178a0323e3b5a9b50ea45e9dd8fa Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Mon, 21 Nov 2016 23:35:10 +0000 Subject: [PATCH 40/97] [ci skip] js-precompiled 20161121-233309 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dd05fc18d..7be09f3e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1263,7 +1263,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#a0cf68024199f7a8c39c7d149ad622ac2f72129a" +source = "git+https://github.com/ethcore/js-precompiled.git#f46188126257e03c775e76a3cea82b5f70549400" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index bed226f02..62a09e1d1 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.60", + "version": "0.2.61", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From bd6f343cbe1f70c4ef5084af4c8d4d8e23297808 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Tue, 22 Nov 2016 11:54:20 +0100 Subject: [PATCH 41/97] DappRegistry (#3405) * Initial version, just loading * Warning dialog * Retrive & show applications * Display completed * Edit/New mode start * Display distry state, resolve hashes to urls * Move buttons to top-level * Rework display * Update error handling & dirty checks * Formatting * Split sections into components * Styling updates * Slight styling adjustments * Start delete modal * Add modals for register & update * Flesh-out register modal * Register error handling * Register registers * List refresh on add & remove * Update operational * Simplify, remove bg image * Really remove bg * fix case * Collapse text-only components --- js/src/dapps/dappreg.html | 17 + js/src/dapps/dappreg.js | 35 ++ .../dapps/dappreg/Application/application.css | 58 +++ .../dapps/dappreg/Application/application.js | 64 +++ js/src/dapps/dappreg/Application/index.js | 17 + js/src/dapps/dappreg/Button/button.css | 38 ++ js/src/dapps/dappreg/Button/button.js | 52 ++ js/src/dapps/dappreg/Button/index.js | 17 + js/src/dapps/dappreg/ButtonBar/buttonBar.css | 21 + js/src/dapps/dappreg/ButtonBar/buttonBar.js | 101 ++++ js/src/dapps/dappreg/ButtonBar/index.js | 17 + js/src/dapps/dappreg/Dapp/dapp.css | 19 + js/src/dapps/dappreg/Dapp/dapp.js | 162 ++++++ js/src/dapps/dappreg/Dapp/index.js | 17 + js/src/dapps/dappreg/Input/index.js | 17 + js/src/dapps/dappreg/Input/input.css | 92 ++++ js/src/dapps/dappreg/Input/input.js | 47 ++ js/src/dapps/dappreg/Modal/index.js | 17 + js/src/dapps/dappreg/Modal/modal.css | 116 +++++ js/src/dapps/dappreg/Modal/modal.js | 66 +++ js/src/dapps/dappreg/ModalDelete/index.js | 17 + .../dapps/dappreg/ModalDelete/modalDelete.js | 159 ++++++ js/src/dapps/dappreg/ModalRegister/index.js | 17 + .../dappreg/ModalRegister/modalRegister.js | 159 ++++++ js/src/dapps/dappreg/ModalUpdate/index.js | 17 + .../dapps/dappreg/ModalUpdate/modalUpdate.js | 169 ++++++ js/src/dapps/dappreg/SelectAccount/index.js | 17 + .../dappreg/SelectAccount/selectAccount.js | 49 ++ js/src/dapps/dappreg/SelectDapp/index.js | 17 + js/src/dapps/dappreg/SelectDapp/selectDapp.js | 76 +++ js/src/dapps/dappreg/Warning/index.js | 17 + js/src/dapps/dappreg/Warning/warning.css | 36 ++ js/src/dapps/dappreg/Warning/warning.js | 51 ++ js/src/dapps/dappreg/dappsStore.js | 482 ++++++++++++++++++ js/src/dapps/dappreg/modalStore.js | 266 ++++++++++ js/src/dapps/dappreg/parity.js | 53 ++ js/webpack.config.js | 1 + 37 files changed, 2593 insertions(+) create mode 100644 js/src/dapps/dappreg.html create mode 100644 js/src/dapps/dappreg.js create mode 100644 js/src/dapps/dappreg/Application/application.css create mode 100644 js/src/dapps/dappreg/Application/application.js create mode 100644 js/src/dapps/dappreg/Application/index.js create mode 100644 js/src/dapps/dappreg/Button/button.css create mode 100644 js/src/dapps/dappreg/Button/button.js create mode 100644 js/src/dapps/dappreg/Button/index.js create mode 100644 js/src/dapps/dappreg/ButtonBar/buttonBar.css create mode 100644 js/src/dapps/dappreg/ButtonBar/buttonBar.js create mode 100644 js/src/dapps/dappreg/ButtonBar/index.js create mode 100644 js/src/dapps/dappreg/Dapp/dapp.css create mode 100644 js/src/dapps/dappreg/Dapp/dapp.js create mode 100644 js/src/dapps/dappreg/Dapp/index.js create mode 100644 js/src/dapps/dappreg/Input/index.js create mode 100644 js/src/dapps/dappreg/Input/input.css create mode 100644 js/src/dapps/dappreg/Input/input.js create mode 100644 js/src/dapps/dappreg/Modal/index.js create mode 100644 js/src/dapps/dappreg/Modal/modal.css create mode 100644 js/src/dapps/dappreg/Modal/modal.js create mode 100644 js/src/dapps/dappreg/ModalDelete/index.js create mode 100644 js/src/dapps/dappreg/ModalDelete/modalDelete.js create mode 100644 js/src/dapps/dappreg/ModalRegister/index.js create mode 100644 js/src/dapps/dappreg/ModalRegister/modalRegister.js create mode 100644 js/src/dapps/dappreg/ModalUpdate/index.js create mode 100644 js/src/dapps/dappreg/ModalUpdate/modalUpdate.js create mode 100644 js/src/dapps/dappreg/SelectAccount/index.js create mode 100644 js/src/dapps/dappreg/SelectAccount/selectAccount.js create mode 100644 js/src/dapps/dappreg/SelectDapp/index.js create mode 100644 js/src/dapps/dappreg/SelectDapp/selectDapp.js create mode 100644 js/src/dapps/dappreg/Warning/index.js create mode 100644 js/src/dapps/dappreg/Warning/warning.css create mode 100644 js/src/dapps/dappreg/Warning/warning.js create mode 100644 js/src/dapps/dappreg/dappsStore.js create mode 100644 js/src/dapps/dappreg/modalStore.js create mode 100644 js/src/dapps/dappreg/parity.js diff --git a/js/src/dapps/dappreg.html b/js/src/dapps/dappreg.html new file mode 100644 index 000000000..89c95c472 --- /dev/null +++ b/js/src/dapps/dappreg.html @@ -0,0 +1,17 @@ + + + + + + + + Dapp Registry + + +
+ + + + + + diff --git a/js/src/dapps/dappreg.js b/js/src/dapps/dappreg.js new file mode 100644 index 000000000..243576a34 --- /dev/null +++ b/js/src/dapps/dappreg.js @@ -0,0 +1,35 @@ +// 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 . + +import React from 'react'; +import ReactDOM from 'react-dom'; +import injectTapEventPlugin from 'react-tap-event-plugin'; +import { useStrict } from 'mobx'; + +injectTapEventPlugin(); +useStrict(true); + +import Application from './dappreg/Application'; + +import '../../assets/fonts/Roboto/font.css'; +import '../../assets/fonts/RobotoMono/font.css'; +import './style.css'; +import './dappreg.html'; + +ReactDOM.render( + , + document.querySelector('#container') +); diff --git a/js/src/dapps/dappreg/Application/application.css b/js/src/dapps/dappreg/Application/application.css new file mode 100644 index 000000000..f171d8127 --- /dev/null +++ b/js/src/dapps/dappreg/Application/application.css @@ -0,0 +1,58 @@ +/* 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 . +*/ + +.body { + color: #333; + background: #eee; + padding: 4.5em 0; + text-align: center; +} + +.apps { + background: #fff; + border-radius: 0.5em; + margin: 0 auto; + max-width: 980px; + padding: 1.5em; + text-align: left; +} + +.footer { + font-size: 0.75em; + margin: 1em; + padding: 1.5em; + text-align: center; +} + +.header { + background: #44e; + border-radius: 0 0 0.25em 0.25em; + color: #fff; + left: 0; + padding: 1em; + position: fixed; + right: 0; + top: 0; + z-index: 25; +} + +.loading { + text-align: center; + padding-top: 5em; + font-size: 2em; + color: #999; +} diff --git a/js/src/dapps/dappreg/Application/application.js b/js/src/dapps/dappreg/Application/application.js new file mode 100644 index 000000000..b5e4d5a97 --- /dev/null +++ b/js/src/dapps/dappreg/Application/application.js @@ -0,0 +1,64 @@ +// 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 . + +import React, { Component } from 'react'; +import { observer } from 'mobx-react'; + +import DappsStore from '../dappsStore'; + +import ButtonBar from '../ButtonBar'; +import Dapp from '../Dapp'; +import ModalDelete from '../ModalDelete'; +import ModalRegister from '../ModalRegister'; +import ModalUpdate from '../ModalUpdate'; +import SelectDapp from '../SelectDapp'; +import Warning from '../Warning'; +import styles from './application.css'; + +@observer +export default class Application extends Component { + dappsStore = DappsStore.instance(); + + render () { + if (this.dappsStore.isLoading) { + return ( +
+ Loading application +
+ ); + } + + return ( +
+
+ DAPP REGISTRY, a global view of distributed applications available on the network. Putting the puzzle together. +
+
+ + + +
+
+ { this.dappsStore.count } applications registered, { this.dappsStore.ownedCount } owned by user +
+ + + + +
+ ); + } +} diff --git a/js/src/dapps/dappreg/Application/index.js b/js/src/dapps/dappreg/Application/index.js new file mode 100644 index 000000000..236578226 --- /dev/null +++ b/js/src/dapps/dappreg/Application/index.js @@ -0,0 +1,17 @@ +// 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 . + +export default from './application'; diff --git a/js/src/dapps/dappreg/Button/button.css b/js/src/dapps/dappreg/Button/button.css new file mode 100644 index 000000000..a66736e46 --- /dev/null +++ b/js/src/dapps/dappreg/Button/button.css @@ -0,0 +1,38 @@ +/* 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 . +*/ + +.button { + background: #44e; + border: none; + border-radius: 0.25em; + color: #fff; + cursor: pointer; + font-size: 1em; + margin: 1em 0.375em; + opacity: 0.85; + padding: 0.75em 2em; + + &[disabled] { + opacity: 0.5; + cursor: default; + background: #aaa; + } + + &[data-warning="true"] { + background: #e44; + } +} diff --git a/js/src/dapps/dappreg/Button/button.js b/js/src/dapps/dappreg/Button/button.js new file mode 100644 index 000000000..1f3b67b4c --- /dev/null +++ b/js/src/dapps/dappreg/Button/button.js @@ -0,0 +1,52 @@ +// 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 . + +import React, { Component, PropTypes } from 'react'; + +import styles from './button.css'; + +export default class Button extends Component { + static propTypes = { + className: PropTypes.string, + disabled: PropTypes.bool, + label: PropTypes.string.isRequired, + warning: PropTypes.bool, + onClick: PropTypes.func.isRequired + } + + render () { + const { className, disabled, label, warning } = this.props; + const classes = `${styles.button} ${className}`; + + return ( + + ); + } + + onClick = (event) => { + if (this.props.disabled) { + return; + } + + this.props.onClick(event); + } +} diff --git a/js/src/dapps/dappreg/Button/index.js b/js/src/dapps/dappreg/Button/index.js new file mode 100644 index 000000000..f69a65e3d --- /dev/null +++ b/js/src/dapps/dappreg/Button/index.js @@ -0,0 +1,17 @@ +// 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 . + +export default from './button'; diff --git a/js/src/dapps/dappreg/ButtonBar/buttonBar.css b/js/src/dapps/dappreg/ButtonBar/buttonBar.css new file mode 100644 index 000000000..0aa84ee29 --- /dev/null +++ b/js/src/dapps/dappreg/ButtonBar/buttonBar.css @@ -0,0 +1,21 @@ +/* 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 . +*/ + +.buttonbar { + text-align: center; + margin: 1em 0 0 0; +} diff --git a/js/src/dapps/dappreg/ButtonBar/buttonBar.js b/js/src/dapps/dappreg/ButtonBar/buttonBar.js new file mode 100644 index 000000000..289def0ea --- /dev/null +++ b/js/src/dapps/dappreg/ButtonBar/buttonBar.js @@ -0,0 +1,101 @@ +// 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 . + +import React, { Component } from 'react'; +import { observer } from 'mobx-react'; + +import DappsStore from '../dappsStore'; +import ModalStore from '../modalStore'; + +import Button from '../Button'; +import styles from './buttonBar.css'; + +@observer +export default class ButtonBar extends Component { + dappsStore = DappsStore.instance(); + modalStore = ModalStore.instance(); + + render () { + let buttons = []; + + if (this.dappsStore.isEditing || this.dappsStore.isNew) { + buttons = [ +