Merge remote-tracking branch 'origin/master' into boxjdb
This commit is contained in:
commit
e461916f5a
@ -44,3 +44,7 @@ travis-nightly = ["ethcore/json-tests", "dev-clippy", "dev"]
|
|||||||
[[bin]]
|
[[bin]]
|
||||||
path = "parity/main.rs"
|
path = "parity/main.rs"
|
||||||
name = "parity"
|
name = "parity"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
debug = false
|
||||||
|
lto = false
|
||||||
|
@ -95,7 +95,7 @@ pub struct BlockQueue {
|
|||||||
panic_handler: Arc<PanicHandler>,
|
panic_handler: Arc<PanicHandler>,
|
||||||
engine: Arc<Box<Engine>>,
|
engine: Arc<Box<Engine>>,
|
||||||
more_to_verify: Arc<Condvar>,
|
more_to_verify: Arc<Condvar>,
|
||||||
verification: Arc<Mutex<Verification>>,
|
verification: Arc<Verification>,
|
||||||
verifiers: Vec<JoinHandle<()>>,
|
verifiers: Vec<JoinHandle<()>>,
|
||||||
deleting: Arc<AtomicBool>,
|
deleting: Arc<AtomicBool>,
|
||||||
ready_signal: Arc<QueueSignal>,
|
ready_signal: Arc<QueueSignal>,
|
||||||
@ -132,18 +132,23 @@ impl QueueSignal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct Verification {
|
struct Verification {
|
||||||
unverified: VecDeque<UnverifiedBlock>,
|
// All locks must be captured in the order declared here.
|
||||||
verified: VecDeque<PreverifiedBlock>,
|
unverified: Mutex<VecDeque<UnverifiedBlock>>,
|
||||||
verifying: VecDeque<VerifyingBlock>,
|
verified: Mutex<VecDeque<PreverifiedBlock>>,
|
||||||
bad: HashSet<H256>,
|
verifying: Mutex<VecDeque<VerifyingBlock>>,
|
||||||
|
bad: Mutex<HashSet<H256>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlockQueue {
|
impl BlockQueue {
|
||||||
/// Creates a new queue instance.
|
/// Creates a new queue instance.
|
||||||
pub fn new(config: BlockQueueConfig, engine: Arc<Box<Engine>>, message_channel: IoChannel<NetSyncMessage>) -> BlockQueue {
|
pub fn new(config: BlockQueueConfig, engine: Arc<Box<Engine>>, message_channel: IoChannel<NetSyncMessage>) -> BlockQueue {
|
||||||
let verification = Arc::new(Mutex::new(Verification::default()));
|
let verification = Arc::new(Verification {
|
||||||
|
unverified: Mutex::new(VecDeque::new()),
|
||||||
|
verified: Mutex::new(VecDeque::new()),
|
||||||
|
verifying: Mutex::new(VecDeque::new()),
|
||||||
|
bad: Mutex::new(HashSet::new()),
|
||||||
|
});
|
||||||
let more_to_verify = Arc::new(Condvar::new());
|
let more_to_verify = Arc::new(Condvar::new());
|
||||||
let ready_signal = Arc::new(QueueSignal { signalled: AtomicBool::new(false), message_channel: message_channel });
|
let ready_signal = Arc::new(QueueSignal { signalled: AtomicBool::new(false), message_channel: message_channel });
|
||||||
let deleting = Arc::new(AtomicBool::new(false));
|
let deleting = Arc::new(AtomicBool::new(false));
|
||||||
@ -186,17 +191,17 @@ impl BlockQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify(verification: Arc<Mutex<Verification>>, engine: Arc<Box<Engine>>, wait: Arc<Condvar>, ready: Arc<QueueSignal>, deleting: Arc<AtomicBool>, empty: Arc<Condvar>) {
|
fn verify(verification: Arc<Verification>, engine: Arc<Box<Engine>>, wait: Arc<Condvar>, ready: Arc<QueueSignal>, deleting: Arc<AtomicBool>, empty: Arc<Condvar>) {
|
||||||
while !deleting.load(AtomicOrdering::Acquire) {
|
while !deleting.load(AtomicOrdering::Acquire) {
|
||||||
{
|
{
|
||||||
let mut lock = verification.lock().unwrap();
|
let mut unverified = verification.unverified.lock().unwrap();
|
||||||
|
|
||||||
if lock.unverified.is_empty() && lock.verifying.is_empty() {
|
if unverified.is_empty() && verification.verifying.lock().unwrap().is_empty() {
|
||||||
empty.notify_all();
|
empty.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
while lock.unverified.is_empty() && !deleting.load(AtomicOrdering::Acquire) {
|
while unverified.is_empty() && !deleting.load(AtomicOrdering::Acquire) {
|
||||||
lock = wait.wait(lock).unwrap();
|
unverified = wait.wait(unverified).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
if deleting.load(AtomicOrdering::Acquire) {
|
if deleting.load(AtomicOrdering::Acquire) {
|
||||||
@ -205,39 +210,42 @@ impl BlockQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let block = {
|
let block = {
|
||||||
let mut v = verification.lock().unwrap();
|
let mut unverified = verification.unverified.lock().unwrap();
|
||||||
if v.unverified.is_empty() {
|
if unverified.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let block = v.unverified.pop_front().unwrap();
|
let mut verifying = verification.verifying.lock().unwrap();
|
||||||
v.verifying.push_back(VerifyingBlock{ hash: block.header.hash(), block: None });
|
let block = unverified.pop_front().unwrap();
|
||||||
|
verifying.push_back(VerifyingBlock{ hash: block.header.hash(), block: None });
|
||||||
block
|
block
|
||||||
};
|
};
|
||||||
|
|
||||||
let block_hash = block.header.hash();
|
let block_hash = block.header.hash();
|
||||||
match verify_block_unordered(block.header, block.bytes, engine.deref().deref()) {
|
match verify_block_unordered(block.header, block.bytes, engine.deref().deref()) {
|
||||||
Ok(verified) => {
|
Ok(verified) => {
|
||||||
let mut v = verification.lock().unwrap();
|
let mut verifying = verification.verifying.lock().unwrap();
|
||||||
for e in &mut v.verifying {
|
for e in verifying.iter_mut() {
|
||||||
if e.hash == block_hash {
|
if e.hash == block_hash {
|
||||||
e.block = Some(verified);
|
e.block = Some(verified);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !v.verifying.is_empty() && v.verifying.front().unwrap().hash == block_hash {
|
if !verifying.is_empty() && verifying.front().unwrap().hash == block_hash {
|
||||||
// we're next!
|
// we're next!
|
||||||
let mut vref = v.deref_mut();
|
let mut verified = verification.verified.lock().unwrap();
|
||||||
BlockQueue::drain_verifying(&mut vref.verifying, &mut vref.verified, &mut vref.bad);
|
let mut bad = verification.bad.lock().unwrap();
|
||||||
|
BlockQueue::drain_verifying(&mut verifying, &mut verified, &mut bad);
|
||||||
ready.set();
|
ready.set();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let mut v = verification.lock().unwrap();
|
let mut verifying = verification.verifying.lock().unwrap();
|
||||||
|
let mut verified = verification.verified.lock().unwrap();
|
||||||
|
let mut bad = verification.bad.lock().unwrap();
|
||||||
warn!(target: "client", "Stage 2 block verification failed for {}\nError: {:?}", block_hash, err);
|
warn!(target: "client", "Stage 2 block verification failed for {}\nError: {:?}", block_hash, err);
|
||||||
v.bad.insert(block_hash.clone());
|
bad.insert(block_hash.clone());
|
||||||
v.verifying.retain(|e| e.hash != block_hash);
|
verifying.retain(|e| e.hash != block_hash);
|
||||||
let mut vref = v.deref_mut();
|
BlockQueue::drain_verifying(&mut verifying, &mut verified, &mut bad);
|
||||||
BlockQueue::drain_verifying(&mut vref.verifying, &mut vref.verified, &mut vref.bad);
|
|
||||||
ready.set();
|
ready.set();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -257,19 +265,21 @@ impl BlockQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Clear the queue and stop verification activity.
|
/// Clear the queue and stop verification activity.
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&self) {
|
||||||
let mut verification = self.verification.lock().unwrap();
|
let mut unverified = self.verification.unverified.lock().unwrap();
|
||||||
verification.unverified.clear();
|
let mut verifying = self.verification.verifying.lock().unwrap();
|
||||||
verification.verifying.clear();
|
let mut verified = self.verification.verified.lock().unwrap();
|
||||||
verification.verified.clear();
|
unverified.clear();
|
||||||
|
verifying.clear();
|
||||||
|
verified.clear();
|
||||||
self.processing.write().unwrap().clear();
|
self.processing.write().unwrap().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wait for queue to be empty
|
/// Wait for unverified queue to be empty
|
||||||
pub fn flush(&mut self) {
|
pub fn flush(&self) {
|
||||||
let mut verification = self.verification.lock().unwrap();
|
let mut unverified = self.verification.unverified.lock().unwrap();
|
||||||
while !verification.unverified.is_empty() || !verification.verifying.is_empty() {
|
while !unverified.is_empty() || !self.verification.verifying.lock().unwrap().is_empty() {
|
||||||
verification = self.empty.wait(verification).unwrap();
|
unverified = self.empty.wait(unverified).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,27 +288,28 @@ impl BlockQueue {
|
|||||||
if self.processing.read().unwrap().contains(&hash) {
|
if self.processing.read().unwrap().contains(&hash) {
|
||||||
return BlockStatus::Queued;
|
return BlockStatus::Queued;
|
||||||
}
|
}
|
||||||
if self.verification.lock().unwrap().bad.contains(&hash) {
|
if self.verification.bad.lock().unwrap().contains(&hash) {
|
||||||
return BlockStatus::Bad;
|
return BlockStatus::Bad;
|
||||||
}
|
}
|
||||||
BlockStatus::Unknown
|
BlockStatus::Unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a block to the queue.
|
/// Add a block to the queue.
|
||||||
pub fn import_block(&mut self, bytes: Bytes) -> ImportResult {
|
pub fn import_block(&self, bytes: Bytes) -> ImportResult {
|
||||||
let header = BlockView::new(&bytes).header();
|
let header = BlockView::new(&bytes).header();
|
||||||
let h = header.hash();
|
let h = header.hash();
|
||||||
|
{
|
||||||
if self.processing.read().unwrap().contains(&h) {
|
if self.processing.read().unwrap().contains(&h) {
|
||||||
return Err(x!(ImportError::AlreadyQueued));
|
return Err(x!(ImportError::AlreadyQueued));
|
||||||
}
|
}
|
||||||
{
|
|
||||||
let mut verification = self.verification.lock().unwrap();
|
let mut bad = self.verification.bad.lock().unwrap();
|
||||||
if verification.bad.contains(&h) {
|
if bad.contains(&h) {
|
||||||
return Err(x!(ImportError::KnownBad));
|
return Err(x!(ImportError::KnownBad));
|
||||||
}
|
}
|
||||||
|
|
||||||
if verification.bad.contains(&header.parent_hash) {
|
if bad.contains(&header.parent_hash) {
|
||||||
verification.bad.insert(h.clone());
|
bad.insert(h.clone());
|
||||||
return Err(x!(ImportError::KnownBad));
|
return Err(x!(ImportError::KnownBad));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -306,48 +317,47 @@ impl BlockQueue {
|
|||||||
match verify_block_basic(&header, &bytes, self.engine.deref().deref()) {
|
match verify_block_basic(&header, &bytes, self.engine.deref().deref()) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
self.processing.write().unwrap().insert(h.clone());
|
self.processing.write().unwrap().insert(h.clone());
|
||||||
self.verification.lock().unwrap().unverified.push_back(UnverifiedBlock { header: header, bytes: bytes });
|
self.verification.unverified.lock().unwrap().push_back(UnverifiedBlock { header: header, bytes: bytes });
|
||||||
self.more_to_verify.notify_all();
|
self.more_to_verify.notify_all();
|
||||||
Ok(h)
|
Ok(h)
|
||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!(target: "client", "Stage 1 block verification failed for {}\nError: {:?}", BlockView::new(&bytes).header_view().sha3(), err);
|
warn!(target: "client", "Stage 1 block verification failed for {}\nError: {:?}", BlockView::new(&bytes).header_view().sha3(), err);
|
||||||
self.verification.lock().unwrap().bad.insert(h.clone());
|
self.verification.bad.lock().unwrap().insert(h.clone());
|
||||||
Err(err)
|
Err(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark given block and all its children as bad. Stops verification.
|
/// Mark given block and all its children as bad. Stops verification.
|
||||||
pub fn mark_as_bad(&mut self, block_hashes: &[H256]) {
|
pub fn mark_as_bad(&self, block_hashes: &[H256]) {
|
||||||
if block_hashes.is_empty() {
|
if block_hashes.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mut verification_lock = self.verification.lock().unwrap();
|
let mut verified_lock = self.verification.verified.lock().unwrap();
|
||||||
|
let mut verified = verified_lock.deref_mut();
|
||||||
|
let mut bad = self.verification.bad.lock().unwrap();
|
||||||
let mut processing = self.processing.write().unwrap();
|
let mut processing = self.processing.write().unwrap();
|
||||||
|
bad.reserve(block_hashes.len());
|
||||||
let mut verification = verification_lock.deref_mut();
|
|
||||||
|
|
||||||
verification.bad.reserve(block_hashes.len());
|
|
||||||
for hash in block_hashes {
|
for hash in block_hashes {
|
||||||
verification.bad.insert(hash.clone());
|
bad.insert(hash.clone());
|
||||||
processing.remove(&hash);
|
processing.remove(&hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut new_verified = VecDeque::new();
|
let mut new_verified = VecDeque::new();
|
||||||
for block in verification.verified.drain(..) {
|
for block in verified.drain(..) {
|
||||||
if verification.bad.contains(&block.header.parent_hash) {
|
if bad.contains(&block.header.parent_hash) {
|
||||||
verification.bad.insert(block.header.hash());
|
bad.insert(block.header.hash());
|
||||||
processing.remove(&block.header.hash());
|
processing.remove(&block.header.hash());
|
||||||
} else {
|
} else {
|
||||||
new_verified.push_back(block);
|
new_verified.push_back(block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
verification.verified = new_verified;
|
*verified = new_verified;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark given block as processed
|
/// Mark given block as processed
|
||||||
pub fn mark_as_good(&mut self, block_hashes: &[H256]) {
|
pub fn mark_as_good(&self, block_hashes: &[H256]) {
|
||||||
if block_hashes.is_empty() {
|
if block_hashes.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -358,16 +368,16 @@ impl BlockQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Removes up to `max` verified blocks from the queue
|
/// Removes up to `max` verified blocks from the queue
|
||||||
pub fn drain(&mut self, max: usize) -> Vec<PreverifiedBlock> {
|
pub fn drain(&self, max: usize) -> Vec<PreverifiedBlock> {
|
||||||
let mut verification = self.verification.lock().unwrap();
|
let mut verified = self.verification.verified.lock().unwrap();
|
||||||
let count = min(max, verification.verified.len());
|
let count = min(max, verified.len());
|
||||||
let mut result = Vec::with_capacity(count);
|
let mut result = Vec::with_capacity(count);
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
let block = verification.verified.pop_front().unwrap();
|
let block = verified.pop_front().unwrap();
|
||||||
result.push(block);
|
result.push(block);
|
||||||
}
|
}
|
||||||
self.ready_signal.reset();
|
self.ready_signal.reset();
|
||||||
if !verification.verified.is_empty() {
|
if !verified.is_empty() {
|
||||||
self.ready_signal.set();
|
self.ready_signal.set();
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
@ -375,17 +385,28 @@ impl BlockQueue {
|
|||||||
|
|
||||||
/// Get queue status.
|
/// Get queue status.
|
||||||
pub fn queue_info(&self) -> BlockQueueInfo {
|
pub fn queue_info(&self) -> BlockQueueInfo {
|
||||||
let verification = self.verification.lock().unwrap();
|
let (unverified_len, unverified_bytes) = {
|
||||||
|
let v = self.verification.unverified.lock().unwrap();
|
||||||
|
(v.len(), v.heap_size_of_children())
|
||||||
|
};
|
||||||
|
let (verifying_len, verifying_bytes) = {
|
||||||
|
let v = self.verification.verifying.lock().unwrap();
|
||||||
|
(v.len(), v.heap_size_of_children())
|
||||||
|
};
|
||||||
|
let (verified_len, verified_bytes) = {
|
||||||
|
let v = self.verification.verified.lock().unwrap();
|
||||||
|
(v.len(), v.heap_size_of_children())
|
||||||
|
};
|
||||||
BlockQueueInfo {
|
BlockQueueInfo {
|
||||||
verified_queue_size: verification.verified.len(),
|
unverified_queue_size: unverified_len,
|
||||||
unverified_queue_size: verification.unverified.len(),
|
verifying_queue_size: verifying_len,
|
||||||
verifying_queue_size: verification.verifying.len(),
|
verified_queue_size: verified_len,
|
||||||
max_queue_size: self.max_queue_size,
|
max_queue_size: self.max_queue_size,
|
||||||
max_mem_use: self.max_mem_use,
|
max_mem_use: self.max_mem_use,
|
||||||
mem_used:
|
mem_used:
|
||||||
verification.unverified.heap_size_of_children()
|
unverified_bytes
|
||||||
+ verification.verifying.heap_size_of_children()
|
+ verifying_bytes
|
||||||
+ verification.verified.heap_size_of_children(),
|
+ verified_bytes
|
||||||
// TODO: https://github.com/servo/heapsize/pull/50
|
// TODO: https://github.com/servo/heapsize/pull/50
|
||||||
//+ self.processing.read().unwrap().heap_size_of_children(),
|
//+ self.processing.read().unwrap().heap_size_of_children(),
|
||||||
}
|
}
|
||||||
@ -393,10 +414,9 @@ impl BlockQueue {
|
|||||||
|
|
||||||
pub fn collect_garbage(&self) {
|
pub fn collect_garbage(&self) {
|
||||||
{
|
{
|
||||||
let mut verification = self.verification.lock().unwrap();
|
self.verification.unverified.lock().unwrap().shrink_to_fit();
|
||||||
verification.unverified.shrink_to_fit();
|
self.verification.verifying.lock().unwrap().shrink_to_fit();
|
||||||
verification.verifying.shrink_to_fit();
|
self.verification.verified.lock().unwrap().shrink_to_fit();
|
||||||
verification.verified.shrink_to_fit();
|
|
||||||
}
|
}
|
||||||
self.processing.write().unwrap().shrink_to_fit();
|
self.processing.write().unwrap().shrink_to_fit();
|
||||||
}
|
}
|
||||||
@ -444,7 +464,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_import_blocks() {
|
fn can_import_blocks() {
|
||||||
let mut queue = get_test_queue();
|
let queue = get_test_queue();
|
||||||
if let Err(e) = queue.import_block(get_good_dummy_block()) {
|
if let Err(e) = queue.import_block(get_good_dummy_block()) {
|
||||||
panic!("error importing block that is valid by definition({:?})", e);
|
panic!("error importing block that is valid by definition({:?})", e);
|
||||||
}
|
}
|
||||||
@ -452,7 +472,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn returns_error_for_duplicates() {
|
fn returns_error_for_duplicates() {
|
||||||
let mut queue = get_test_queue();
|
let queue = get_test_queue();
|
||||||
if let Err(e) = queue.import_block(get_good_dummy_block()) {
|
if let Err(e) = queue.import_block(get_good_dummy_block()) {
|
||||||
panic!("error importing block that is valid by definition({:?})", e);
|
panic!("error importing block that is valid by definition({:?})", e);
|
||||||
}
|
}
|
||||||
@ -471,7 +491,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn returns_ok_for_drained_duplicates() {
|
fn returns_ok_for_drained_duplicates() {
|
||||||
let mut queue = get_test_queue();
|
let queue = get_test_queue();
|
||||||
let block = get_good_dummy_block();
|
let block = get_good_dummy_block();
|
||||||
let hash = BlockView::new(&block).header().hash().clone();
|
let hash = BlockView::new(&block).header().hash().clone();
|
||||||
if let Err(e) = queue.import_block(block) {
|
if let Err(e) = queue.import_block(block) {
|
||||||
@ -488,7 +508,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn returns_empty_once_finished() {
|
fn returns_empty_once_finished() {
|
||||||
let mut queue = get_test_queue();
|
let queue = get_test_queue();
|
||||||
queue.import_block(get_good_dummy_block()).expect("error importing block that is valid by definition");
|
queue.import_block(get_good_dummy_block()).expect("error importing block that is valid by definition");
|
||||||
queue.flush();
|
queue.flush();
|
||||||
queue.drain(1);
|
queue.drain(1);
|
||||||
|
@ -18,6 +18,7 @@ use util::numbers::{U256,H256};
|
|||||||
use header::BlockNumber;
|
use header::BlockNumber;
|
||||||
|
|
||||||
/// Brief info about inserted block.
|
/// Brief info about inserted block.
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct BlockInfo {
|
pub struct BlockInfo {
|
||||||
/// Block hash.
|
/// Block hash.
|
||||||
pub hash: H256,
|
pub hash: H256,
|
||||||
@ -30,6 +31,7 @@ pub struct BlockInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Describes location of newly inserted block.
|
/// Describes location of newly inserted block.
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum BlockLocation {
|
pub enum BlockLocation {
|
||||||
/// It's part of the canon chain.
|
/// It's part of the canon chain.
|
||||||
CanonChain,
|
CanonChain,
|
||||||
@ -42,6 +44,8 @@ pub enum BlockLocation {
|
|||||||
/// Hash of the newest common ancestor with old canon chain.
|
/// Hash of the newest common ancestor with old canon chain.
|
||||||
ancestor: H256,
|
ancestor: H256,
|
||||||
/// Hashes of the blocks between ancestor and this block.
|
/// Hashes of the blocks between ancestor and this block.
|
||||||
route: Vec<H256>
|
enacted: Vec<H256>,
|
||||||
|
/// Hashes of the blocks which were invalidated.
|
||||||
|
retracted: Vec<H256>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
//! Blockchain database.
|
//! Blockchain database.
|
||||||
|
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrder};
|
||||||
use util::*;
|
use util::*;
|
||||||
use header::*;
|
use header::*;
|
||||||
use extras::*;
|
use extras::*;
|
||||||
@ -28,7 +29,7 @@ use blockchain::best_block::BestBlock;
|
|||||||
use blockchain::bloom_indexer::BloomIndexer;
|
use blockchain::bloom_indexer::BloomIndexer;
|
||||||
use blockchain::tree_route::TreeRoute;
|
use blockchain::tree_route::TreeRoute;
|
||||||
use blockchain::update::ExtrasUpdate;
|
use blockchain::update::ExtrasUpdate;
|
||||||
use blockchain::CacheSize;
|
use blockchain::{CacheSize, ImportRoute};
|
||||||
|
|
||||||
const BLOOM_INDEX_SIZE: usize = 16;
|
const BLOOM_INDEX_SIZE: usize = 16;
|
||||||
const BLOOM_LEVELS: u8 = 3;
|
const BLOOM_LEVELS: u8 = 3;
|
||||||
@ -134,8 +135,9 @@ struct CacheManager {
|
|||||||
///
|
///
|
||||||
/// **Does not do input data verification.**
|
/// **Does not do input data verification.**
|
||||||
pub struct BlockChain {
|
pub struct BlockChain {
|
||||||
pref_cache_size: usize,
|
// All locks must be captured in the order declared here.
|
||||||
max_cache_size: usize,
|
pref_cache_size: AtomicUsize,
|
||||||
|
max_cache_size: AtomicUsize,
|
||||||
|
|
||||||
best_block: RwLock<BestBlock>,
|
best_block: RwLock<BestBlock>,
|
||||||
|
|
||||||
@ -157,6 +159,8 @@ pub struct BlockChain {
|
|||||||
|
|
||||||
// blooms indexing
|
// blooms indexing
|
||||||
bloom_indexer: BloomIndexer,
|
bloom_indexer: BloomIndexer,
|
||||||
|
|
||||||
|
insert_lock: Mutex<()>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FilterDataSource for BlockChain {
|
impl FilterDataSource for BlockChain {
|
||||||
@ -262,8 +266,8 @@ impl BlockChain {
|
|||||||
(0..COLLECTION_QUEUE_SIZE).foreach(|_| cache_man.cache_usage.push_back(HashSet::new()));
|
(0..COLLECTION_QUEUE_SIZE).foreach(|_| cache_man.cache_usage.push_back(HashSet::new()));
|
||||||
|
|
||||||
let bc = BlockChain {
|
let bc = BlockChain {
|
||||||
pref_cache_size: config.pref_cache_size,
|
pref_cache_size: AtomicUsize::new(config.pref_cache_size),
|
||||||
max_cache_size: config.max_cache_size,
|
max_cache_size: AtomicUsize::new(config.max_cache_size),
|
||||||
best_block: RwLock::new(BestBlock::default()),
|
best_block: RwLock::new(BestBlock::default()),
|
||||||
blocks: RwLock::new(HashMap::new()),
|
blocks: RwLock::new(HashMap::new()),
|
||||||
block_details: RwLock::new(HashMap::new()),
|
block_details: RwLock::new(HashMap::new()),
|
||||||
@ -275,7 +279,8 @@ impl BlockChain {
|
|||||||
extras_db: extras_db,
|
extras_db: extras_db,
|
||||||
blocks_db: blocks_db,
|
blocks_db: blocks_db,
|
||||||
cache_man: RwLock::new(cache_man),
|
cache_man: RwLock::new(cache_man),
|
||||||
bloom_indexer: BloomIndexer::new(BLOOM_INDEX_SIZE, BLOOM_LEVELS)
|
bloom_indexer: BloomIndexer::new(BLOOM_INDEX_SIZE, BLOOM_LEVELS),
|
||||||
|
insert_lock: Mutex::new(()),
|
||||||
};
|
};
|
||||||
|
|
||||||
// load best block
|
// load best block
|
||||||
@ -318,9 +323,9 @@ impl BlockChain {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set the cache configuration.
|
/// Set the cache configuration.
|
||||||
pub fn configure_cache(&mut self, pref_cache_size: usize, max_cache_size: usize) {
|
pub fn configure_cache(&self, pref_cache_size: usize, max_cache_size: usize) {
|
||||||
self.pref_cache_size = pref_cache_size;
|
self.pref_cache_size.store(pref_cache_size, AtomicOrder::Relaxed);
|
||||||
self.max_cache_size = max_cache_size;
|
self.max_cache_size.store(max_cache_size, AtomicOrder::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a tree route between `from` and `to`, which is a tuple of:
|
/// Returns a tree route between `from` and `to`, which is a tuple of:
|
||||||
@ -414,16 +419,17 @@ impl BlockChain {
|
|||||||
/// Inserts the block into backing cache database.
|
/// Inserts the block into backing cache database.
|
||||||
/// Expects the block to be valid and already verified.
|
/// Expects the block to be valid and already verified.
|
||||||
/// If the block is already known, does nothing.
|
/// If the block is already known, does nothing.
|
||||||
pub fn insert_block(&self, bytes: &[u8], receipts: Vec<Receipt>) {
|
pub fn insert_block(&self, bytes: &[u8], receipts: Vec<Receipt>) -> ImportRoute {
|
||||||
// create views onto rlp
|
// create views onto rlp
|
||||||
let block = BlockView::new(bytes);
|
let block = BlockView::new(bytes);
|
||||||
let header = block.header_view();
|
let header = block.header_view();
|
||||||
let hash = header.sha3();
|
let hash = header.sha3();
|
||||||
|
|
||||||
if self.is_known(&hash) {
|
if self.is_known(&hash) {
|
||||||
return;
|
return ImportRoute::none();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _lock = self.insert_lock.lock();
|
||||||
// store block in db
|
// store block in db
|
||||||
self.blocks_db.put(&hash, &bytes).unwrap();
|
self.blocks_db.put(&hash, &bytes).unwrap();
|
||||||
|
|
||||||
@ -435,8 +441,10 @@ impl BlockChain {
|
|||||||
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
block_receipts: self.prepare_block_receipts_update(receipts, &info),
|
||||||
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
|
transactions_addresses: self.prepare_transaction_addresses_update(bytes, &info),
|
||||||
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
|
blocks_blooms: self.prepare_block_blooms_update(bytes, &info),
|
||||||
info: info
|
info: info.clone(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ImportRoute::from(info)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Applies extras update.
|
/// Applies extras update.
|
||||||
@ -444,8 +452,38 @@ impl BlockChain {
|
|||||||
let batch = DBTransaction::new();
|
let batch = DBTransaction::new();
|
||||||
batch.put(b"best", &update.info.hash).unwrap();
|
batch.put(b"best", &update.info.hash).unwrap();
|
||||||
|
|
||||||
// update best block
|
{
|
||||||
|
let mut write_details = self.block_details.write().unwrap();
|
||||||
|
for (hash, details) in update.block_details.into_iter() {
|
||||||
|
batch.put_extras(&hash, &details);
|
||||||
|
self.note_used(CacheID::Extras(ExtrasIndex::BlockDetails, hash.clone()));
|
||||||
|
write_details.insert(hash, details);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut write_receipts = self.block_receipts.write().unwrap();
|
||||||
|
for (hash, receipt) in &update.block_receipts {
|
||||||
|
batch.put_extras(hash, receipt);
|
||||||
|
write_receipts.remove(hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut write_blocks_blooms = self.blocks_blooms.write().unwrap();
|
||||||
|
for (bloom_hash, blocks_bloom) in &update.blocks_blooms {
|
||||||
|
batch.put_extras(bloom_hash, blocks_bloom);
|
||||||
|
write_blocks_blooms.remove(bloom_hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// These cached values must be updated last and togeterh
|
||||||
|
{
|
||||||
let mut best_block = self.best_block.write().unwrap();
|
let mut best_block = self.best_block.write().unwrap();
|
||||||
|
let mut write_hashes = self.block_hashes.write().unwrap();
|
||||||
|
let mut write_txs = self.transaction_addresses.write().unwrap();
|
||||||
|
|
||||||
|
// update best block
|
||||||
match update.info.location {
|
match update.info.location {
|
||||||
BlockLocation::Branch => (),
|
BlockLocation::Branch => (),
|
||||||
_ => {
|
_ => {
|
||||||
@ -457,35 +495,15 @@ impl BlockChain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut write_hashes = self.block_hashes.write().unwrap();
|
|
||||||
for (number, hash) in &update.block_hashes {
|
for (number, hash) in &update.block_hashes {
|
||||||
batch.put_extras(number, hash);
|
batch.put_extras(number, hash);
|
||||||
write_hashes.remove(number);
|
write_hashes.remove(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut write_details = self.block_details.write().unwrap();
|
|
||||||
for (hash, details) in update.block_details.into_iter() {
|
|
||||||
batch.put_extras(&hash, &details);
|
|
||||||
write_details.insert(hash.clone(), details);
|
|
||||||
self.note_used(CacheID::Extras(ExtrasIndex::BlockDetails, hash));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut write_receipts = self.block_receipts.write().unwrap();
|
|
||||||
for (hash, receipt) in &update.block_receipts {
|
|
||||||
batch.put_extras(hash, receipt);
|
|
||||||
write_receipts.remove(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut write_txs = self.transaction_addresses.write().unwrap();
|
|
||||||
for (hash, tx_address) in &update.transactions_addresses {
|
for (hash, tx_address) in &update.transactions_addresses {
|
||||||
batch.put_extras(hash, tx_address);
|
batch.put_extras(hash, tx_address);
|
||||||
write_txs.remove(hash);
|
write_txs.remove(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut write_blocks_blooms = self.blocks_blooms.write().unwrap();
|
|
||||||
for (bloom_hash, blocks_bloom) in &update.blocks_blooms {
|
|
||||||
batch.put_extras(bloom_hash, blocks_bloom);
|
|
||||||
write_blocks_blooms.remove(bloom_hash);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// update extras database
|
// update extras database
|
||||||
@ -549,9 +567,14 @@ impl BlockChain {
|
|||||||
|
|
||||||
match route.blocks.len() {
|
match route.blocks.len() {
|
||||||
0 => BlockLocation::CanonChain,
|
0 => BlockLocation::CanonChain,
|
||||||
_ => BlockLocation::BranchBecomingCanonChain {
|
_ => {
|
||||||
|
let retracted = route.blocks.iter().take(route.index).cloned().collect::<Vec<H256>>();
|
||||||
|
|
||||||
|
BlockLocation::BranchBecomingCanonChain {
|
||||||
ancestor: route.ancestor,
|
ancestor: route.ancestor,
|
||||||
route: route.blocks.into_iter().skip(route.index).collect()
|
enacted: route.blocks.into_iter().skip(route.index).collect(),
|
||||||
|
retracted: retracted.into_iter().rev().collect(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -572,11 +595,11 @@ impl BlockChain {
|
|||||||
BlockLocation::CanonChain => {
|
BlockLocation::CanonChain => {
|
||||||
block_hashes.insert(number, info.hash.clone());
|
block_hashes.insert(number, info.hash.clone());
|
||||||
},
|
},
|
||||||
BlockLocation::BranchBecomingCanonChain { ref ancestor, ref route } => {
|
BlockLocation::BranchBecomingCanonChain { ref ancestor, ref enacted, .. } => {
|
||||||
let ancestor_number = self.block_number(ancestor).unwrap();
|
let ancestor_number = self.block_number(ancestor).unwrap();
|
||||||
let start_number = ancestor_number + 1;
|
let start_number = ancestor_number + 1;
|
||||||
|
|
||||||
for (index, hash) in route.iter().cloned().enumerate() {
|
for (index, hash) in enacted.iter().cloned().enumerate() {
|
||||||
block_hashes.insert(start_number + index as BlockNumber, hash);
|
block_hashes.insert(start_number + index as BlockNumber, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -661,11 +684,11 @@ impl BlockChain {
|
|||||||
ChainFilter::new(self, self.bloom_indexer.index_size(), self.bloom_indexer.levels())
|
ChainFilter::new(self, self.bloom_indexer.index_size(), self.bloom_indexer.levels())
|
||||||
.add_bloom(&header.log_bloom(), header.number() as usize)
|
.add_bloom(&header.log_bloom(), header.number() as usize)
|
||||||
},
|
},
|
||||||
BlockLocation::BranchBecomingCanonChain { ref ancestor, ref route } => {
|
BlockLocation::BranchBecomingCanonChain { ref ancestor, ref enacted, .. } => {
|
||||||
let ancestor_number = self.block_number(ancestor).unwrap();
|
let ancestor_number = self.block_number(ancestor).unwrap();
|
||||||
let start_number = ancestor_number + 1;
|
let start_number = ancestor_number + 1;
|
||||||
|
|
||||||
let mut blooms: Vec<H2048> = route.iter()
|
let mut blooms: Vec<H2048> = enacted.iter()
|
||||||
.map(|hash| self.block(hash).unwrap())
|
.map(|hash| self.block(hash).unwrap())
|
||||||
.map(|bytes| BlockView::new(&bytes).header_view().log_bloom())
|
.map(|bytes| BlockView::new(&bytes).header_view().log_bloom())
|
||||||
.collect();
|
.collect();
|
||||||
@ -774,11 +797,10 @@ impl BlockChain {
|
|||||||
|
|
||||||
/// Ticks our cache system and throws out any old data.
|
/// Ticks our cache system and throws out any old data.
|
||||||
pub fn collect_garbage(&self) {
|
pub fn collect_garbage(&self) {
|
||||||
if self.cache_size().total() < self.pref_cache_size { return; }
|
if self.cache_size().total() < self.pref_cache_size.load(AtomicOrder::Relaxed) { return; }
|
||||||
|
|
||||||
for _ in 0..COLLECTION_QUEUE_SIZE {
|
for _ in 0..COLLECTION_QUEUE_SIZE {
|
||||||
{
|
{
|
||||||
let mut cache_man = self.cache_man.write().unwrap();
|
|
||||||
let mut blocks = self.blocks.write().unwrap();
|
let mut blocks = self.blocks.write().unwrap();
|
||||||
let mut block_details = self.block_details.write().unwrap();
|
let mut block_details = self.block_details.write().unwrap();
|
||||||
let mut block_hashes = self.block_hashes.write().unwrap();
|
let mut block_hashes = self.block_hashes.write().unwrap();
|
||||||
@ -786,6 +808,7 @@ impl BlockChain {
|
|||||||
let mut block_logs = self.block_logs.write().unwrap();
|
let mut block_logs = self.block_logs.write().unwrap();
|
||||||
let mut blocks_blooms = self.blocks_blooms.write().unwrap();
|
let mut blocks_blooms = self.blocks_blooms.write().unwrap();
|
||||||
let mut block_receipts = self.block_receipts.write().unwrap();
|
let mut block_receipts = self.block_receipts.write().unwrap();
|
||||||
|
let mut cache_man = self.cache_man.write().unwrap();
|
||||||
|
|
||||||
for id in cache_man.cache_usage.pop_back().unwrap().into_iter() {
|
for id in cache_man.cache_usage.pop_back().unwrap().into_iter() {
|
||||||
cache_man.in_use.remove(&id);
|
cache_man.in_use.remove(&id);
|
||||||
@ -812,7 +835,7 @@ impl BlockChain {
|
|||||||
blocks_blooms.shrink_to_fit();
|
blocks_blooms.shrink_to_fit();
|
||||||
block_receipts.shrink_to_fit();
|
block_receipts.shrink_to_fit();
|
||||||
}
|
}
|
||||||
if self.cache_size().total() < self.max_cache_size { break; }
|
if self.cache_size().total() < self.max_cache_size.load(AtomicOrder::Relaxed) { break; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: m_lastCollection = chrono::system_clock::now();
|
// TODO: m_lastCollection = chrono::system_clock::now();
|
||||||
@ -825,7 +848,7 @@ mod tests {
|
|||||||
use rustc_serialize::hex::FromHex;
|
use rustc_serialize::hex::FromHex;
|
||||||
use util::hash::*;
|
use util::hash::*;
|
||||||
use util::sha3::Hashable;
|
use util::sha3::Hashable;
|
||||||
use blockchain::{BlockProvider, BlockChain, BlockChainConfig};
|
use blockchain::{BlockProvider, BlockChain, BlockChainConfig, ImportRoute};
|
||||||
use tests::helpers::*;
|
use tests::helpers::*;
|
||||||
use devtools::*;
|
use devtools::*;
|
||||||
use blockchain::generator::{ChainGenerator, ChainIterator, BlockFinalizer};
|
use blockchain::generator::{ChainGenerator, ChainIterator, BlockFinalizer};
|
||||||
@ -943,10 +966,30 @@ mod tests {
|
|||||||
|
|
||||||
let temp = RandomTempPath::new();
|
let temp = RandomTempPath::new();
|
||||||
let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
|
let bc = BlockChain::new(BlockChainConfig::default(), &genesis, temp.as_path());
|
||||||
bc.insert_block(&b1, vec![]);
|
let ir1 = bc.insert_block(&b1, vec![]);
|
||||||
bc.insert_block(&b2, vec![]);
|
let ir2 = bc.insert_block(&b2, vec![]);
|
||||||
bc.insert_block(&b3a, vec![]);
|
let ir3b = bc.insert_block(&b3b, vec![]);
|
||||||
bc.insert_block(&b3b, vec![]);
|
let ir3a = bc.insert_block(&b3a, vec![]);
|
||||||
|
|
||||||
|
assert_eq!(ir1, ImportRoute {
|
||||||
|
enacted: vec![b1_hash],
|
||||||
|
retracted: vec![],
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(ir2, ImportRoute {
|
||||||
|
enacted: vec![b2_hash],
|
||||||
|
retracted: vec![],
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(ir3b, ImportRoute {
|
||||||
|
enacted: vec![b3b_hash],
|
||||||
|
retracted: vec![],
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(ir3a, ImportRoute {
|
||||||
|
enacted: vec![b3a_hash],
|
||||||
|
retracted: vec![b3b_hash],
|
||||||
|
});
|
||||||
|
|
||||||
assert_eq!(bc.best_block_hash(), best_block_hash);
|
assert_eq!(bc.best_block_hash(), best_block_hash);
|
||||||
assert_eq!(bc.block_number(&genesis_hash).unwrap(), 0);
|
assert_eq!(bc.block_number(&genesis_hash).unwrap(), 0);
|
||||||
|
119
ethcore/src/blockchain/import_route.rs
Normal file
119
ethcore/src/blockchain/import_route.rs
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Import route.
|
||||||
|
|
||||||
|
use util::hash::H256;
|
||||||
|
use blockchain::block_info::{BlockInfo, BlockLocation};
|
||||||
|
|
||||||
|
/// Import route for newly inserted block.
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct ImportRoute {
|
||||||
|
/// Blocks that were invalidated by new block.
|
||||||
|
pub retracted: Vec<H256>,
|
||||||
|
/// Blocks that were validated by new block.
|
||||||
|
pub enacted: Vec<H256>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImportRoute {
|
||||||
|
pub fn none() -> Self {
|
||||||
|
ImportRoute {
|
||||||
|
retracted: vec![],
|
||||||
|
enacted: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BlockInfo> for ImportRoute {
|
||||||
|
fn from(info: BlockInfo) -> ImportRoute {
|
||||||
|
match info.location {
|
||||||
|
BlockLocation::CanonChain => ImportRoute {
|
||||||
|
retracted: vec![],
|
||||||
|
enacted: vec![info.hash],
|
||||||
|
},
|
||||||
|
BlockLocation::Branch => ImportRoute::none(),
|
||||||
|
BlockLocation::BranchBecomingCanonChain { mut enacted, retracted, .. } => {
|
||||||
|
enacted.push(info.hash);
|
||||||
|
ImportRoute {
|
||||||
|
retracted: retracted,
|
||||||
|
enacted: enacted,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use util::hash::H256;
|
||||||
|
use util::numbers::U256;
|
||||||
|
use blockchain::block_info::{BlockInfo, BlockLocation};
|
||||||
|
use blockchain::ImportRoute;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn import_route_none() {
|
||||||
|
assert_eq!(ImportRoute::none(), ImportRoute {
|
||||||
|
enacted: vec![],
|
||||||
|
retracted: vec![],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn import_route_branch() {
|
||||||
|
let info = BlockInfo {
|
||||||
|
hash: H256::from(U256::from(1)),
|
||||||
|
number: 0,
|
||||||
|
total_difficulty: U256::from(0),
|
||||||
|
location: BlockLocation::Branch,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(ImportRoute::from(info), ImportRoute::none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn import_route_canon_chain() {
|
||||||
|
let info = BlockInfo {
|
||||||
|
hash: H256::from(U256::from(1)),
|
||||||
|
number: 0,
|
||||||
|
total_difficulty: U256::from(0),
|
||||||
|
location: BlockLocation::CanonChain,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(ImportRoute::from(info), ImportRoute {
|
||||||
|
retracted: vec![],
|
||||||
|
enacted: vec![H256::from(U256::from(1))],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn import_route_branch_becoming_canon_chain() {
|
||||||
|
let info = BlockInfo {
|
||||||
|
hash: H256::from(U256::from(2)),
|
||||||
|
number: 0,
|
||||||
|
total_difficulty: U256::from(0),
|
||||||
|
location: BlockLocation::BranchBecomingCanonChain {
|
||||||
|
ancestor: H256::from(U256::from(0)),
|
||||||
|
enacted: vec![H256::from(U256::from(1))],
|
||||||
|
retracted: vec![H256::from(U256::from(3)), H256::from(U256::from(4))],
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(ImportRoute::from(info), ImportRoute {
|
||||||
|
retracted: vec![H256::from(U256::from(3)), H256::from(U256::from(4))],
|
||||||
|
enacted: vec![H256::from(U256::from(1)), H256::from(U256::from(2))],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -23,9 +23,11 @@ mod bloom_indexer;
|
|||||||
mod cache;
|
mod cache;
|
||||||
mod tree_route;
|
mod tree_route;
|
||||||
mod update;
|
mod update;
|
||||||
|
mod import_route;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod generator;
|
mod generator;
|
||||||
|
|
||||||
pub use self::blockchain::{BlockProvider, BlockChain, BlockChainConfig};
|
pub use self::blockchain::{BlockProvider, BlockChain, BlockChainConfig};
|
||||||
pub use self::cache::CacheSize;
|
pub use self::cache::CacheSize;
|
||||||
pub use self::tree_route::TreeRoute;
|
pub use self::tree_route::TreeRoute;
|
||||||
|
pub use self::import_route::ImportRoute;
|
||||||
|
@ -20,7 +20,6 @@ use std::marker::PhantomData;
|
|||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
use util::*;
|
use util::*;
|
||||||
use util::panics::*;
|
use util::panics::*;
|
||||||
use blockchain::{BlockChain, BlockProvider};
|
|
||||||
use views::BlockView;
|
use views::BlockView;
|
||||||
use error::*;
|
use error::*;
|
||||||
use header::{BlockNumber};
|
use header::{BlockNumber};
|
||||||
@ -28,7 +27,6 @@ use state::State;
|
|||||||
use spec::Spec;
|
use spec::Spec;
|
||||||
use engine::Engine;
|
use engine::Engine;
|
||||||
use views::HeaderView;
|
use views::HeaderView;
|
||||||
use block_queue::BlockQueue;
|
|
||||||
use service::{NetSyncMessage, SyncMessage};
|
use service::{NetSyncMessage, SyncMessage};
|
||||||
use env_info::LastHashes;
|
use env_info::LastHashes;
|
||||||
use verification::*;
|
use verification::*;
|
||||||
@ -37,33 +35,10 @@ use transaction::LocalizedTransaction;
|
|||||||
use extras::TransactionAddress;
|
use extras::TransactionAddress;
|
||||||
use filter::Filter;
|
use filter::Filter;
|
||||||
use log_entry::LocalizedLogEntry;
|
use log_entry::LocalizedLogEntry;
|
||||||
use util::keys::store::SecretStore;
|
use block_queue::{BlockQueue, BlockQueueInfo};
|
||||||
pub use block_queue::{BlockQueueConfig, BlockQueueInfo};
|
use blockchain::{BlockChain, BlockProvider, TreeRoute};
|
||||||
pub use blockchain::{TreeRoute, BlockChainConfig, CacheSize as BlockChainCacheSize};
|
use client::{BlockId, TransactionId, ClientConfig, BlockChainClient};
|
||||||
|
pub use blockchain::CacheSize as BlockChainCacheSize;
|
||||||
/// Uniquely identifies block.
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
pub enum BlockId {
|
|
||||||
/// Block's sha3.
|
|
||||||
/// Querying by hash is always faster.
|
|
||||||
Hash(H256),
|
|
||||||
/// Block number within canon blockchain.
|
|
||||||
Number(BlockNumber),
|
|
||||||
/// Earliest block (genesis).
|
|
||||||
Earliest,
|
|
||||||
/// Latest mined block.
|
|
||||||
Latest
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Uniquely identifies transaction.
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
pub enum TransactionId {
|
|
||||||
/// Transaction's sha3.
|
|
||||||
Hash(H256),
|
|
||||||
/// Block id and transaction index within this block.
|
|
||||||
/// Querying by block position is always faster.
|
|
||||||
Location(BlockId, usize)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// General block status
|
/// General block status
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
@ -78,30 +53,6 @@ pub enum BlockStatus {
|
|||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Client configuration. Includes configs for all sub-systems.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ClientConfig {
|
|
||||||
/// Block queue configuration.
|
|
||||||
pub queue: BlockQueueConfig,
|
|
||||||
/// Blockchain configuration.
|
|
||||||
pub blockchain: BlockChainConfig,
|
|
||||||
/// Prefer journal rather than archive.
|
|
||||||
pub prefer_journal: bool,
|
|
||||||
/// The name of the client instance.
|
|
||||||
pub name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ClientConfig {
|
|
||||||
fn default() -> ClientConfig {
|
|
||||||
ClientConfig {
|
|
||||||
queue: Default::default(),
|
|
||||||
blockchain: Default::default(),
|
|
||||||
prefer_journal: false,
|
|
||||||
name: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Information about the blockchain gathered together.
|
/// Information about the blockchain gathered together.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BlockChainInfo {
|
pub struct BlockChainInfo {
|
||||||
@ -123,72 +74,8 @@ impl fmt::Display for BlockChainInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Blockchain database client. Owns and manages a blockchain and a block queue.
|
|
||||||
pub trait BlockChainClient : Sync + Send {
|
|
||||||
/// Get raw block header data by block id.
|
|
||||||
fn block_header(&self, id: BlockId) -> Option<Bytes>;
|
|
||||||
|
|
||||||
/// Get raw block body data by block id.
|
|
||||||
/// Block body is an RLP list of two items: uncles and transactions.
|
|
||||||
fn block_body(&self, id: BlockId) -> Option<Bytes>;
|
|
||||||
|
|
||||||
/// Get raw block data by block header hash.
|
|
||||||
fn block(&self, id: BlockId) -> Option<Bytes>;
|
|
||||||
|
|
||||||
/// Get block status by block header hash.
|
|
||||||
fn block_status(&self, id: BlockId) -> BlockStatus;
|
|
||||||
|
|
||||||
/// Get block total difficulty.
|
|
||||||
fn block_total_difficulty(&self, id: BlockId) -> Option<U256>;
|
|
||||||
|
|
||||||
/// Get address nonce.
|
|
||||||
fn nonce(&self, address: &Address) -> U256;
|
|
||||||
|
|
||||||
/// Get block hash.
|
|
||||||
fn block_hash(&self, id: BlockId) -> Option<H256>;
|
|
||||||
|
|
||||||
/// Get address code.
|
|
||||||
fn code(&self, address: &Address) -> Option<Bytes>;
|
|
||||||
|
|
||||||
/// Get transaction with given hash.
|
|
||||||
fn transaction(&self, id: TransactionId) -> Option<LocalizedTransaction>;
|
|
||||||
|
|
||||||
/// Get a tree route between `from` and `to`.
|
|
||||||
/// See `BlockChain::tree_route`.
|
|
||||||
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute>;
|
|
||||||
|
|
||||||
/// Get latest state node
|
|
||||||
fn state_data(&self, hash: &H256) -> Option<Bytes>;
|
|
||||||
|
|
||||||
/// Get raw block receipts data by block header hash.
|
|
||||||
fn block_receipts(&self, hash: &H256) -> Option<Bytes>;
|
|
||||||
|
|
||||||
/// Import a block into the blockchain.
|
|
||||||
fn import_block(&self, bytes: Bytes) -> ImportResult;
|
|
||||||
|
|
||||||
/// Get block queue information.
|
|
||||||
fn queue_info(&self) -> BlockQueueInfo;
|
|
||||||
|
|
||||||
/// Clear block queue and abort all import activity.
|
|
||||||
fn clear_queue(&self);
|
|
||||||
|
|
||||||
/// Get blockchain information.
|
|
||||||
fn chain_info(&self) -> BlockChainInfo;
|
|
||||||
|
|
||||||
/// Get the best block header.
|
|
||||||
fn best_block_header(&self) -> Bytes {
|
|
||||||
self.block_header(BlockId::Hash(self.chain_info().best_block_hash)).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns numbers of blocks containing given bloom.
|
|
||||||
fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockId, to_block: BlockId) -> Option<Vec<BlockNumber>>;
|
|
||||||
|
|
||||||
/// Returns logs matching given filter.
|
|
||||||
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Clone, Debug, Eq, PartialEq)]
|
|
||||||
/// Report on the status of a client.
|
/// Report on the status of a client.
|
||||||
|
#[derive(Default, Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct ClientReport {
|
pub struct ClientReport {
|
||||||
/// How many blocks have been imported so far.
|
/// How many blocks have been imported so far.
|
||||||
pub blocks_imported: usize,
|
pub blocks_imported: usize,
|
||||||
@ -212,10 +99,10 @@ impl ClientReport {
|
|||||||
/// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue.
|
/// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue.
|
||||||
/// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue.
|
/// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue.
|
||||||
pub struct Client<V = CanonVerifier> where V: Verifier {
|
pub struct Client<V = CanonVerifier> where V: Verifier {
|
||||||
chain: Arc<RwLock<BlockChain>>,
|
chain: Arc<BlockChain>,
|
||||||
engine: Arc<Box<Engine>>,
|
engine: Arc<Box<Engine>>,
|
||||||
state_db: Mutex<Box<JournalDB + Send>>,
|
state_db: Mutex<Box<JournalDB + Send>>,
|
||||||
block_queue: RwLock<BlockQueue>,
|
block_queue: BlockQueue,
|
||||||
report: RwLock<ClientReport>,
|
report: RwLock<ClientReport>,
|
||||||
import_lock: Mutex<()>,
|
import_lock: Mutex<()>,
|
||||||
panic_handler: Arc<PanicHandler>,
|
panic_handler: Arc<PanicHandler>,
|
||||||
@ -226,7 +113,6 @@ pub struct Client<V = CanonVerifier> where V: Verifier {
|
|||||||
author: RwLock<Address>,
|
author: RwLock<Address>,
|
||||||
extra_data: RwLock<Bytes>,
|
extra_data: RwLock<Bytes>,
|
||||||
verifier: PhantomData<V>,
|
verifier: PhantomData<V>,
|
||||||
secret_store: Arc<RwLock<SecretStore>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const HISTORY: u64 = 1000;
|
const HISTORY: u64 = 1000;
|
||||||
@ -248,7 +134,7 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
dir.push(format!("v{}-sec-{}", CLIENT_DB_VER_STR, if config.prefer_journal { "pruned" } else { "archive" }));
|
dir.push(format!("v{}-sec-{}", CLIENT_DB_VER_STR, if config.prefer_journal { "pruned" } else { "archive" }));
|
||||||
let path = dir.as_path();
|
let path = dir.as_path();
|
||||||
let gb = spec.genesis_block();
|
let gb = spec.genesis_block();
|
||||||
let chain = Arc::new(RwLock::new(BlockChain::new(config.blockchain, &gb, path)));
|
let chain = Arc::new(BlockChain::new(config.blockchain, &gb, path));
|
||||||
let mut state_path = path.to_path_buf();
|
let mut state_path = path.to_path_buf();
|
||||||
state_path.push("state");
|
state_path.push("state");
|
||||||
|
|
||||||
@ -262,14 +148,11 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
let panic_handler = PanicHandler::new_in_arc();
|
let panic_handler = PanicHandler::new_in_arc();
|
||||||
panic_handler.forward_from(&block_queue);
|
panic_handler.forward_from(&block_queue);
|
||||||
|
|
||||||
let secret_store = Arc::new(RwLock::new(SecretStore::new()));
|
|
||||||
secret_store.write().unwrap().try_import_existing();
|
|
||||||
|
|
||||||
Ok(Arc::new(Client {
|
Ok(Arc::new(Client {
|
||||||
chain: chain,
|
chain: chain,
|
||||||
engine: engine,
|
engine: engine,
|
||||||
state_db: Mutex::new(state_db),
|
state_db: Mutex::new(state_db),
|
||||||
block_queue: RwLock::new(block_queue),
|
block_queue: block_queue,
|
||||||
report: RwLock::new(Default::default()),
|
report: RwLock::new(Default::default()),
|
||||||
import_lock: Mutex::new(()),
|
import_lock: Mutex::new(()),
|
||||||
panic_handler: panic_handler,
|
panic_handler: panic_handler,
|
||||||
@ -278,22 +161,20 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
author: RwLock::new(Address::new()),
|
author: RwLock::new(Address::new()),
|
||||||
extra_data: RwLock::new(Vec::new()),
|
extra_data: RwLock::new(Vec::new()),
|
||||||
verifier: PhantomData,
|
verifier: PhantomData,
|
||||||
secret_store: secret_store,
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Flush the block import queue.
|
/// Flush the block import queue.
|
||||||
pub fn flush_queue(&self) {
|
pub fn flush_queue(&self) {
|
||||||
self.block_queue.write().unwrap().flush();
|
self.block_queue.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_last_hashes(&self, parent_hash: H256) -> LastHashes {
|
fn build_last_hashes(&self, parent_hash: H256) -> LastHashes {
|
||||||
let mut last_hashes = LastHashes::new();
|
let mut last_hashes = LastHashes::new();
|
||||||
last_hashes.resize(256, H256::new());
|
last_hashes.resize(256, H256::new());
|
||||||
last_hashes[0] = parent_hash;
|
last_hashes[0] = parent_hash;
|
||||||
let chain = self.chain.read().unwrap();
|
|
||||||
for i in 0..255 {
|
for i in 0..255 {
|
||||||
match chain.block_details(&last_hashes[i]) {
|
match self.chain.block_details(&last_hashes[i]) {
|
||||||
Some(details) => {
|
Some(details) => {
|
||||||
last_hashes[i + 1] = details.parent.clone();
|
last_hashes[i + 1] = details.parent.clone();
|
||||||
},
|
},
|
||||||
@ -303,31 +184,26 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
last_hashes
|
last_hashes
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Secret store (key manager)
|
|
||||||
pub fn secret_store(&self) -> &Arc<RwLock<SecretStore>> {
|
|
||||||
&self.secret_store
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_and_close_block(&self, block: &PreverifiedBlock) -> Result<ClosedBlock, ()> {
|
fn check_and_close_block(&self, block: &PreverifiedBlock) -> Result<ClosedBlock, ()> {
|
||||||
let engine = self.engine.deref().deref();
|
let engine = self.engine.deref().deref();
|
||||||
let header = &block.header;
|
let header = &block.header;
|
||||||
|
|
||||||
// Check the block isn't so old we won't be able to enact it.
|
// Check the block isn't so old we won't be able to enact it.
|
||||||
let best_block_number = self.chain.read().unwrap().best_block_number();
|
let best_block_number = self.chain.best_block_number();
|
||||||
if best_block_number >= HISTORY && header.number() <= best_block_number - HISTORY {
|
if best_block_number >= HISTORY && header.number() <= best_block_number - HISTORY {
|
||||||
warn!(target: "client", "Block import failed for #{} ({})\nBlock is ancient (current best block: #{}).", header.number(), header.hash(), best_block_number);
|
warn!(target: "client", "Block import failed for #{} ({})\nBlock is ancient (current best block: #{}).", header.number(), header.hash(), best_block_number);
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify Block Family
|
// Verify Block Family
|
||||||
let verify_family_result = V::verify_block_family(&header, &block.bytes, engine, self.chain.read().unwrap().deref());
|
let verify_family_result = V::verify_block_family(&header, &block.bytes, engine, self.chain.deref());
|
||||||
if let Err(e) = verify_family_result {
|
if let Err(e) = verify_family_result {
|
||||||
warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
|
||||||
return Err(());
|
return Err(());
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if Parent is in chain
|
// Check if Parent is in chain
|
||||||
let chain_has_parent = self.chain.read().unwrap().block_header(&header.parent_hash);
|
let chain_has_parent = self.chain.block_header(&header.parent_hash);
|
||||||
if let None = chain_has_parent {
|
if let None = chain_has_parent {
|
||||||
warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash);
|
warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash);
|
||||||
return Err(());
|
return Err(());
|
||||||
@ -362,7 +238,7 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
let mut bad_blocks = HashSet::new();
|
let mut bad_blocks = HashSet::new();
|
||||||
|
|
||||||
let _import_lock = self.import_lock.lock();
|
let _import_lock = self.import_lock.lock();
|
||||||
let blocks = self.block_queue.write().unwrap().drain(max_blocks_to_import);
|
let blocks = self.block_queue.drain(max_blocks_to_import);
|
||||||
|
|
||||||
let original_best = self.chain_info().best_block_hash;
|
let original_best = self.chain_info().best_block_hash;
|
||||||
|
|
||||||
@ -383,8 +259,7 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
// Are we committing an era?
|
// Are we committing an era?
|
||||||
let ancient = if header.number() >= HISTORY {
|
let ancient = if header.number() >= HISTORY {
|
||||||
let n = header.number() - HISTORY;
|
let n = header.number() - HISTORY;
|
||||||
let chain = self.chain.read().unwrap();
|
Some((n, self.chain.block_hash(n).unwrap()))
|
||||||
Some((n, chain.block_hash(n).unwrap()))
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@ -396,9 +271,9 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
.commit(header.number(), &header.hash(), ancient)
|
.commit(header.number(), &header.hash(), ancient)
|
||||||
.expect("State DB commit failed.");
|
.expect("State DB commit failed.");
|
||||||
|
|
||||||
// And update the chain
|
// And update the chain after commit to prevent race conditions
|
||||||
self.chain.write().unwrap()
|
// (when something is in chain but you are not able to fetch details)
|
||||||
.insert_block(&block.bytes, receipts);
|
self.chain.insert_block(&block.bytes, receipts);
|
||||||
|
|
||||||
self.report.write().unwrap().accrue_block(&block);
|
self.report.write().unwrap().accrue_block(&block);
|
||||||
trace!(target: "client", "Imported #{} ({})", header.number(), header.hash());
|
trace!(target: "client", "Imported #{} ({})", header.number(), header.hash());
|
||||||
@ -408,18 +283,16 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
let bad_blocks = bad_blocks.into_iter().collect::<Vec<H256>>();
|
let bad_blocks = bad_blocks.into_iter().collect::<Vec<H256>>();
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut block_queue = self.block_queue.write().unwrap();
|
|
||||||
if !bad_blocks.is_empty() {
|
if !bad_blocks.is_empty() {
|
||||||
block_queue.mark_as_bad(&bad_blocks);
|
self.block_queue.mark_as_bad(&bad_blocks);
|
||||||
}
|
}
|
||||||
if !good_blocks.is_empty() {
|
if !good_blocks.is_empty() {
|
||||||
block_queue.mark_as_good(&good_blocks);
|
self.block_queue.mark_as_good(&good_blocks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let block_queue = self.block_queue.read().unwrap();
|
if !good_blocks.is_empty() && self.block_queue.queue_info().is_empty() {
|
||||||
if !good_blocks.is_empty() && block_queue.queue_info().is_empty() {
|
|
||||||
io.send(NetworkIoMessage::User(SyncMessage::NewChainBlocks {
|
io.send(NetworkIoMessage::User(SyncMessage::NewChainBlocks {
|
||||||
good: good_blocks,
|
good: good_blocks,
|
||||||
bad: bad_blocks,
|
bad: bad_blocks,
|
||||||
@ -443,7 +316,7 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
|
|
||||||
/// Get info on the cache.
|
/// Get info on the cache.
|
||||||
pub fn blockchain_cache_info(&self) -> BlockChainCacheSize {
|
pub fn blockchain_cache_info(&self) -> BlockChainCacheSize {
|
||||||
self.chain.read().unwrap().cache_size()
|
self.chain.cache_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the report.
|
/// Get the report.
|
||||||
@ -455,13 +328,13 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
|
|
||||||
/// Tick the client.
|
/// Tick the client.
|
||||||
pub fn tick(&self) {
|
pub fn tick(&self) {
|
||||||
self.chain.read().unwrap().collect_garbage();
|
self.chain.collect_garbage();
|
||||||
self.block_queue.read().unwrap().collect_garbage();
|
self.block_queue.collect_garbage();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set up the cache behaviour.
|
/// Set up the cache behaviour.
|
||||||
pub fn configure_cache(&self, pref_cache_size: usize, max_cache_size: usize) {
|
pub fn configure_cache(&self, pref_cache_size: usize, max_cache_size: usize) {
|
||||||
self.chain.write().unwrap().configure_cache(pref_cache_size, max_cache_size);
|
self.chain.configure_cache(pref_cache_size, max_cache_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_hash(chain: &BlockChain, id: BlockId) -> Option<H256> {
|
fn block_hash(chain: &BlockChain, id: BlockId) -> Option<H256> {
|
||||||
@ -476,9 +349,9 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
fn block_number(&self, id: BlockId) -> Option<BlockNumber> {
|
fn block_number(&self, id: BlockId) -> Option<BlockNumber> {
|
||||||
match id {
|
match id {
|
||||||
BlockId::Number(number) => Some(number),
|
BlockId::Number(number) => Some(number),
|
||||||
BlockId::Hash(ref hash) => self.chain.read().unwrap().block_number(hash),
|
BlockId::Hash(ref hash) => self.chain.block_number(hash),
|
||||||
BlockId::Earliest => Some(0),
|
BlockId::Earliest => Some(0),
|
||||||
BlockId::Latest => Some(self.chain.read().unwrap().best_block_number())
|
BlockId::Latest => Some(self.chain.best_block_number())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,17 +377,17 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
|
|
||||||
/// New chain head event. Restart mining operation.
|
/// New chain head event. Restart mining operation.
|
||||||
pub fn prepare_sealing(&self) {
|
pub fn prepare_sealing(&self) {
|
||||||
let h = self.chain.read().unwrap().best_block_hash();
|
let h = self.chain.best_block_hash();
|
||||||
let mut b = OpenBlock::new(
|
let mut b = OpenBlock::new(
|
||||||
self.engine.deref().deref(),
|
self.engine.deref().deref(),
|
||||||
self.state_db.lock().unwrap().spawn(),
|
self.state_db.lock().unwrap().spawn(),
|
||||||
match self.chain.read().unwrap().block_header(&h) { Some(ref x) => x, None => {return;} },
|
match self.chain.block_header(&h) { Some(ref x) => x, None => {return;} },
|
||||||
self.build_last_hashes(h.clone()),
|
self.build_last_hashes(h.clone()),
|
||||||
self.author(),
|
self.author(),
|
||||||
self.extra_data()
|
self.extra_data()
|
||||||
);
|
);
|
||||||
|
|
||||||
self.chain.read().unwrap().find_uncle_headers(&h, self.engine.deref().deref().maximum_uncle_age()).unwrap().into_iter().take(self.engine.deref().deref().maximum_uncle_count()).foreach(|h| { b.push_uncle(h).unwrap(); });
|
self.chain.find_uncle_headers(&h, self.engine.deref().deref().maximum_uncle_age()).unwrap().into_iter().take(self.engine.deref().deref().maximum_uncle_count()).foreach(|h| { b.push_uncle(h).unwrap(); });
|
||||||
|
|
||||||
// TODO: push transactions.
|
// TODO: push transactions.
|
||||||
|
|
||||||
@ -522,53 +395,18 @@ impl<V> Client<V> where V: Verifier {
|
|||||||
trace!("Sealing: number={}, hash={}, diff={}", b.hash(), b.block().header().difficulty(), b.block().header().number());
|
trace!("Sealing: number={}, hash={}, diff={}", b.hash(), b.block().header().difficulty(), b.block().header().number());
|
||||||
*self.sealing_block.lock().unwrap() = Some(b);
|
*self.sealing_block.lock().unwrap() = Some(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Grab the `ClosedBlock` that we want to be sealed. Comes as a mutex that you have to lock.
|
|
||||||
pub fn sealing_block(&self) -> &Mutex<Option<ClosedBlock>> {
|
|
||||||
if self.sealing_block.lock().unwrap().is_none() {
|
|
||||||
self.sealing_enabled.store(true, atomic::Ordering::Relaxed);
|
|
||||||
// TODO: Above should be on a timer that resets after two blocks have arrived without being asked for.
|
|
||||||
self.prepare_sealing();
|
|
||||||
}
|
|
||||||
&self.sealing_block
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Submit `seal` as a valid solution for the header of `pow_hash`.
|
|
||||||
/// Will check the seal, but not actually insert the block into the chain.
|
|
||||||
pub fn submit_seal(&self, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error> {
|
|
||||||
let mut maybe_b = self.sealing_block.lock().unwrap();
|
|
||||||
match *maybe_b {
|
|
||||||
Some(ref b) if b.hash() == pow_hash => {}
|
|
||||||
_ => { return Err(Error::PowHashInvalid); }
|
|
||||||
}
|
|
||||||
|
|
||||||
let b = maybe_b.take();
|
|
||||||
match b.unwrap().try_seal(self.engine.deref().deref(), seal) {
|
|
||||||
Err(old) => {
|
|
||||||
*maybe_b = Some(old);
|
|
||||||
Err(Error::PowInvalid)
|
|
||||||
}
|
|
||||||
Ok(sealed) => {
|
|
||||||
// TODO: commit DB from `sealed.drain` and make a VerifiedBlock to skip running the transactions twice.
|
|
||||||
try!(self.import_block(sealed.rlp_bytes()));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: need MinerService MinerIoHandler
|
// TODO: need MinerService MinerIoHandler
|
||||||
|
|
||||||
impl<V> BlockChainClient for Client<V> where V: Verifier {
|
impl<V> BlockChainClient for Client<V> where V: Verifier {
|
||||||
fn block_header(&self, id: BlockId) -> Option<Bytes> {
|
fn block_header(&self, id: BlockId) -> Option<Bytes> {
|
||||||
let chain = self.chain.read().unwrap();
|
Self::block_hash(&self.chain, id).and_then(|hash| self.chain.block(&hash).map(|bytes| BlockView::new(&bytes).rlp().at(0).as_raw().to_vec()))
|
||||||
Self::block_hash(&chain, id).and_then(|hash| chain.block(&hash).map(|bytes| BlockView::new(&bytes).rlp().at(0).as_raw().to_vec()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_body(&self, id: BlockId) -> Option<Bytes> {
|
fn block_body(&self, id: BlockId) -> Option<Bytes> {
|
||||||
let chain = self.chain.read().unwrap();
|
Self::block_hash(&self.chain, id).and_then(|hash| {
|
||||||
Self::block_hash(&chain, id).and_then(|hash| {
|
self.chain.block(&hash).map(|bytes| {
|
||||||
chain.block(&hash).map(|bytes| {
|
|
||||||
let rlp = Rlp::new(&bytes);
|
let rlp = Rlp::new(&bytes);
|
||||||
let mut body = RlpStream::new_list(2);
|
let mut body = RlpStream::new_list(2);
|
||||||
body.append_raw(rlp.at(1).as_raw(), 1);
|
body.append_raw(rlp.at(1).as_raw(), 1);
|
||||||
@ -579,24 +417,21 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn block(&self, id: BlockId) -> Option<Bytes> {
|
fn block(&self, id: BlockId) -> Option<Bytes> {
|
||||||
let chain = self.chain.read().unwrap();
|
Self::block_hash(&self.chain, id).and_then(|hash| {
|
||||||
Self::block_hash(&chain, id).and_then(|hash| {
|
self.chain.block(&hash)
|
||||||
chain.block(&hash)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_status(&self, id: BlockId) -> BlockStatus {
|
fn block_status(&self, id: BlockId) -> BlockStatus {
|
||||||
let chain = self.chain.read().unwrap();
|
match Self::block_hash(&self.chain, id) {
|
||||||
match Self::block_hash(&chain, id) {
|
Some(ref hash) if self.chain.is_known(hash) => BlockStatus::InChain,
|
||||||
Some(ref hash) if chain.is_known(hash) => BlockStatus::InChain,
|
Some(hash) => self.block_queue.block_status(&hash),
|
||||||
Some(hash) => self.block_queue.read().unwrap().block_status(&hash),
|
|
||||||
None => BlockStatus::Unknown
|
None => BlockStatus::Unknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_total_difficulty(&self, id: BlockId) -> Option<U256> {
|
fn block_total_difficulty(&self, id: BlockId) -> Option<U256> {
|
||||||
let chain = self.chain.read().unwrap();
|
Self::block_hash(&self.chain, id).and_then(|hash| self.chain.block_details(&hash)).map(|d| d.total_difficulty)
|
||||||
Self::block_hash(&chain, id).and_then(|hash| chain.block_details(&hash)).map(|d| d.total_difficulty)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn nonce(&self, address: &Address) -> U256 {
|
fn nonce(&self, address: &Address) -> U256 {
|
||||||
@ -604,8 +439,7 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn block_hash(&self, id: BlockId) -> Option<H256> {
|
fn block_hash(&self, id: BlockId) -> Option<H256> {
|
||||||
let chain = self.chain.read().unwrap();
|
Self::block_hash(&self.chain, id)
|
||||||
Self::block_hash(&chain, id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn code(&self, address: &Address) -> Option<Bytes> {
|
fn code(&self, address: &Address) -> Option<Bytes> {
|
||||||
@ -613,20 +447,18 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn transaction(&self, id: TransactionId) -> Option<LocalizedTransaction> {
|
fn transaction(&self, id: TransactionId) -> Option<LocalizedTransaction> {
|
||||||
let chain = self.chain.read().unwrap();
|
|
||||||
match id {
|
match id {
|
||||||
TransactionId::Hash(ref hash) => chain.transaction_address(hash),
|
TransactionId::Hash(ref hash) => self.chain.transaction_address(hash),
|
||||||
TransactionId::Location(id, index) => Self::block_hash(&chain, id).map(|hash| TransactionAddress {
|
TransactionId::Location(id, index) => Self::block_hash(&self.chain, id).map(|hash| TransactionAddress {
|
||||||
block_hash: hash,
|
block_hash: hash,
|
||||||
index: index
|
index: index
|
||||||
})
|
})
|
||||||
}.and_then(|address| chain.transaction(&address))
|
}.and_then(|address| self.chain.transaction(&address))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute> {
|
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute> {
|
||||||
let chain = self.chain.read().unwrap();
|
match self.chain.is_known(from) && self.chain.is_known(to) {
|
||||||
match chain.is_known(from) && chain.is_known(to) {
|
true => Some(self.chain.tree_route(from.clone(), to.clone())),
|
||||||
true => Some(chain.tree_route(from.clone(), to.clone())),
|
|
||||||
false => None
|
false => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -642,43 +474,44 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
|
|||||||
fn import_block(&self, bytes: Bytes) -> ImportResult {
|
fn import_block(&self, bytes: Bytes) -> ImportResult {
|
||||||
{
|
{
|
||||||
let header = BlockView::new(&bytes).header_view();
|
let header = BlockView::new(&bytes).header_view();
|
||||||
if self.chain.read().unwrap().is_known(&header.sha3()) {
|
if self.chain.is_known(&header.sha3()) {
|
||||||
return Err(x!(ImportError::AlreadyInChain));
|
return Err(x!(ImportError::AlreadyInChain));
|
||||||
}
|
}
|
||||||
if self.block_status(BlockId::Hash(header.parent_hash())) == BlockStatus::Unknown {
|
if self.block_status(BlockId::Hash(header.parent_hash())) == BlockStatus::Unknown {
|
||||||
return Err(x!(BlockError::UnknownParent(header.parent_hash())));
|
return Err(x!(BlockError::UnknownParent(header.parent_hash())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.block_queue.write().unwrap().import_block(bytes)
|
self.block_queue.import_block(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn queue_info(&self) -> BlockQueueInfo {
|
fn queue_info(&self) -> BlockQueueInfo {
|
||||||
self.block_queue.read().unwrap().queue_info()
|
self.block_queue.queue_info()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_queue(&self) {
|
fn clear_queue(&self) {
|
||||||
self.block_queue.write().unwrap().clear();
|
self.block_queue.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chain_info(&self) -> BlockChainInfo {
|
fn chain_info(&self) -> BlockChainInfo {
|
||||||
let chain = self.chain.read().unwrap();
|
|
||||||
BlockChainInfo {
|
BlockChainInfo {
|
||||||
total_difficulty: chain.best_block_total_difficulty(),
|
total_difficulty: self.chain.best_block_total_difficulty(),
|
||||||
pending_total_difficulty: chain.best_block_total_difficulty(),
|
pending_total_difficulty: self.chain.best_block_total_difficulty(),
|
||||||
genesis_hash: chain.genesis_hash(),
|
genesis_hash: self.chain.genesis_hash(),
|
||||||
best_block_hash: chain.best_block_hash(),
|
best_block_hash: self.chain.best_block_hash(),
|
||||||
best_block_number: From::from(chain.best_block_number())
|
best_block_number: From::from(self.chain.best_block_number())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockId, to_block: BlockId) -> Option<Vec<BlockNumber>> {
|
fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockId, to_block: BlockId) -> Option<Vec<BlockNumber>> {
|
||||||
match (self.block_number(from_block), self.block_number(to_block)) {
|
match (self.block_number(from_block), self.block_number(to_block)) {
|
||||||
(Some(from), Some(to)) => Some(self.chain.read().unwrap().blocks_with_bloom(bloom, from, to)),
|
(Some(from), Some(to)) => Some(self.chain.blocks_with_bloom(bloom, from, to)),
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry> {
|
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry> {
|
||||||
|
// TODO: lock blockchain only once
|
||||||
|
|
||||||
let mut blocks = filter.bloom_possibilities().iter()
|
let mut blocks = filter.bloom_possibilities().iter()
|
||||||
.filter_map(|bloom| self.blocks_with_bloom(bloom, filter.from_block.clone(), filter.to_block.clone()))
|
.filter_map(|bloom| self.blocks_with_bloom(bloom, filter.from_block.clone(), filter.to_block.clone()))
|
||||||
.flat_map(|m| m)
|
.flat_map(|m| m)
|
||||||
@ -690,9 +523,9 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
|
|||||||
blocks.sort();
|
blocks.sort();
|
||||||
|
|
||||||
blocks.into_iter()
|
blocks.into_iter()
|
||||||
.filter_map(|number| self.chain.read().unwrap().block_hash(number).map(|hash| (number, hash)))
|
.filter_map(|number| self.chain.block_hash(number).map(|hash| (number, hash)))
|
||||||
.filter_map(|(number, hash)| self.chain.read().unwrap().block_receipts(&hash).map(|r| (number, hash, r.receipts)))
|
.filter_map(|(number, hash)| self.chain.block_receipts(&hash).map(|r| (number, hash, r.receipts)))
|
||||||
.filter_map(|(number, hash, receipts)| self.chain.read().unwrap().block(&hash).map(|ref b| (number, hash, receipts, BlockView::new(b).transaction_hashes())))
|
.filter_map(|(number, hash, receipts)| self.chain.block(&hash).map(|ref b| (number, hash, receipts, BlockView::new(b).transaction_hashes())))
|
||||||
.flat_map(|(number, hash, receipts, hashes)| {
|
.flat_map(|(number, hash, receipts, hashes)| {
|
||||||
let mut log_index = 0;
|
let mut log_index = 0;
|
||||||
receipts.into_iter()
|
receipts.into_iter()
|
||||||
@ -717,6 +550,39 @@ impl<V> BlockChainClient for Client<V> where V: Verifier {
|
|||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Grab the `ClosedBlock` that we want to be sealed. Comes as a mutex that you have to lock.
|
||||||
|
fn sealing_block(&self) -> &Mutex<Option<ClosedBlock>> {
|
||||||
|
if self.sealing_block.lock().unwrap().is_none() {
|
||||||
|
self.sealing_enabled.store(true, atomic::Ordering::Relaxed);
|
||||||
|
// TODO: Above should be on a timer that resets after two blocks have arrived without being asked for.
|
||||||
|
self.prepare_sealing();
|
||||||
|
}
|
||||||
|
&self.sealing_block
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Submit `seal` as a valid solution for the header of `pow_hash`.
|
||||||
|
/// Will check the seal, but not actually insert the block into the chain.
|
||||||
|
fn submit_seal(&self, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error> {
|
||||||
|
let mut maybe_b = self.sealing_block.lock().unwrap();
|
||||||
|
match *maybe_b {
|
||||||
|
Some(ref b) if b.hash() == pow_hash => {}
|
||||||
|
_ => { return Err(Error::PowHashInvalid); }
|
||||||
|
}
|
||||||
|
|
||||||
|
let b = maybe_b.take();
|
||||||
|
match b.unwrap().try_seal(self.engine.deref().deref(), seal) {
|
||||||
|
Err(old) => {
|
||||||
|
*maybe_b = Some(old);
|
||||||
|
Err(Error::PowInvalid)
|
||||||
|
}
|
||||||
|
Ok(sealed) => {
|
||||||
|
// TODO: commit DB from `sealed.drain` and make a VerifiedBlock to skip running the transactions twice.
|
||||||
|
try!(self.import_block(sealed.rlp_bytes()));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MayPanic for Client {
|
impl MayPanic for Client {
|
31
ethcore/src/client/config.rs
Normal file
31
ethcore/src/client/config.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
pub use block_queue::BlockQueueConfig;
|
||||||
|
pub use blockchain::BlockChainConfig;
|
||||||
|
|
||||||
|
/// Client configuration. Includes configs for all sub-systems.
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct ClientConfig {
|
||||||
|
/// Block queue configuration.
|
||||||
|
pub queue: BlockQueueConfig,
|
||||||
|
/// Blockchain configuration.
|
||||||
|
pub blockchain: BlockChainConfig,
|
||||||
|
/// Prefer journal rather than archive.
|
||||||
|
pub prefer_journal: bool,
|
||||||
|
/// The name of the client instance.
|
||||||
|
pub name: String,
|
||||||
|
}
|
44
ethcore/src/client/ids.rs
Normal file
44
ethcore/src/client/ids.rs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Unique identifiers.
|
||||||
|
|
||||||
|
use util::hash::H256;
|
||||||
|
use header::BlockNumber;
|
||||||
|
|
||||||
|
/// Uniquely identifies block.
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum BlockId {
|
||||||
|
/// Block's sha3.
|
||||||
|
/// Querying by hash is always faster.
|
||||||
|
Hash(H256),
|
||||||
|
/// Block number within canon blockchain.
|
||||||
|
Number(BlockNumber),
|
||||||
|
/// Earliest block (genesis).
|
||||||
|
Earliest,
|
||||||
|
/// Latest mined block.
|
||||||
|
Latest
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uniquely identifies transaction.
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum TransactionId {
|
||||||
|
/// Transaction's sha3.
|
||||||
|
Hash(H256),
|
||||||
|
/// Block id and transaction index within this block.
|
||||||
|
/// Querying by block position is always faster.
|
||||||
|
Location(BlockId, usize)
|
||||||
|
}
|
113
ethcore/src/client/mod.rs
Normal file
113
ethcore/src/client/mod.rs
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Blockchain database client.
|
||||||
|
|
||||||
|
mod client;
|
||||||
|
mod config;
|
||||||
|
mod ids;
|
||||||
|
mod test_client;
|
||||||
|
|
||||||
|
pub use self::client::*;
|
||||||
|
pub use self::config::{ClientConfig, BlockQueueConfig, BlockChainConfig};
|
||||||
|
pub use self::ids::{BlockId, TransactionId};
|
||||||
|
pub use self::test_client::{TestBlockChainClient, EachBlockWith};
|
||||||
|
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use util::bytes::Bytes;
|
||||||
|
use util::hash::{Address, H256, H2048};
|
||||||
|
use util::numbers::U256;
|
||||||
|
use blockchain::TreeRoute;
|
||||||
|
use block_queue::BlockQueueInfo;
|
||||||
|
use block::ClosedBlock;
|
||||||
|
use header::BlockNumber;
|
||||||
|
use transaction::LocalizedTransaction;
|
||||||
|
use log_entry::LocalizedLogEntry;
|
||||||
|
use filter::Filter;
|
||||||
|
use error::{ImportResult, Error};
|
||||||
|
|
||||||
|
/// Blockchain database client. Owns and manages a blockchain and a block queue.
|
||||||
|
pub trait BlockChainClient : Sync + Send {
|
||||||
|
/// Get raw block header data by block id.
|
||||||
|
fn block_header(&self, id: BlockId) -> Option<Bytes>;
|
||||||
|
|
||||||
|
/// Get raw block body data by block id.
|
||||||
|
/// Block body is an RLP list of two items: uncles and transactions.
|
||||||
|
fn block_body(&self, id: BlockId) -> Option<Bytes>;
|
||||||
|
|
||||||
|
/// Get raw block data by block header hash.
|
||||||
|
fn block(&self, id: BlockId) -> Option<Bytes>;
|
||||||
|
|
||||||
|
/// Get block status by block header hash.
|
||||||
|
fn block_status(&self, id: BlockId) -> BlockStatus;
|
||||||
|
|
||||||
|
/// Get block total difficulty.
|
||||||
|
fn block_total_difficulty(&self, id: BlockId) -> Option<U256>;
|
||||||
|
|
||||||
|
/// Get address nonce.
|
||||||
|
fn nonce(&self, address: &Address) -> U256;
|
||||||
|
|
||||||
|
/// Get block hash.
|
||||||
|
fn block_hash(&self, id: BlockId) -> Option<H256>;
|
||||||
|
|
||||||
|
/// Get address code.
|
||||||
|
fn code(&self, address: &Address) -> Option<Bytes>;
|
||||||
|
|
||||||
|
/// Get transaction with given hash.
|
||||||
|
fn transaction(&self, id: TransactionId) -> Option<LocalizedTransaction>;
|
||||||
|
|
||||||
|
/// Get a tree route between `from` and `to`.
|
||||||
|
/// See `BlockChain::tree_route`.
|
||||||
|
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute>;
|
||||||
|
|
||||||
|
/// Get latest state node
|
||||||
|
fn state_data(&self, hash: &H256) -> Option<Bytes>;
|
||||||
|
|
||||||
|
/// Get raw block receipts data by block header hash.
|
||||||
|
fn block_receipts(&self, hash: &H256) -> Option<Bytes>;
|
||||||
|
|
||||||
|
/// Import a block into the blockchain.
|
||||||
|
fn import_block(&self, bytes: Bytes) -> ImportResult;
|
||||||
|
|
||||||
|
/// Get block queue information.
|
||||||
|
fn queue_info(&self) -> BlockQueueInfo;
|
||||||
|
|
||||||
|
/// Clear block queue and abort all import activity.
|
||||||
|
fn clear_queue(&self);
|
||||||
|
|
||||||
|
/// Get blockchain information.
|
||||||
|
fn chain_info(&self) -> BlockChainInfo;
|
||||||
|
|
||||||
|
/// Get the best block header.
|
||||||
|
fn best_block_header(&self) -> Bytes {
|
||||||
|
// TODO: lock blockchain only once
|
||||||
|
self.block_header(BlockId::Hash(self.chain_info().best_block_hash)).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns numbers of blocks containing given bloom.
|
||||||
|
fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockId, to_block: BlockId) -> Option<Vec<BlockNumber>>;
|
||||||
|
|
||||||
|
/// Returns logs matching given filter.
|
||||||
|
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry>;
|
||||||
|
|
||||||
|
/// Grab the `ClosedBlock` that we want to be sealed. Comes as a mutex that you have to lock.
|
||||||
|
fn sealing_block(&self) -> &Mutex<Option<ClosedBlock>>;
|
||||||
|
|
||||||
|
/// Submit `seal` as a valid solution for the header of `pow_hash`.
|
||||||
|
/// Will check the seal, but not actually insert the block into the chain.
|
||||||
|
fn submit_seal(&self, pow_hash: H256, seal: Vec<Bytes>) -> Result<(), Error>;
|
||||||
|
}
|
||||||
|
|
336
ethcore/src/client/test_client.rs
Normal file
336
ethcore/src/client/test_client.rs
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Test client.
|
||||||
|
|
||||||
|
use util::*;
|
||||||
|
use transaction::{Transaction, LocalizedTransaction, Action};
|
||||||
|
use blockchain::TreeRoute;
|
||||||
|
use client::{BlockChainClient, BlockChainInfo, BlockStatus, BlockId, TransactionId};
|
||||||
|
use header::{Header as BlockHeader, BlockNumber};
|
||||||
|
use filter::Filter;
|
||||||
|
use log_entry::LocalizedLogEntry;
|
||||||
|
use receipt::Receipt;
|
||||||
|
use error::{ImportResult, Error};
|
||||||
|
use block_queue::BlockQueueInfo;
|
||||||
|
use block::ClosedBlock;
|
||||||
|
|
||||||
|
/// Test client.
|
||||||
|
pub struct TestBlockChainClient {
|
||||||
|
/// Blocks.
|
||||||
|
pub blocks: RwLock<HashMap<H256, Bytes>>,
|
||||||
|
/// Mapping of numbers to hashes.
|
||||||
|
pub numbers: RwLock<HashMap<usize, H256>>,
|
||||||
|
/// Genesis block hash.
|
||||||
|
pub genesis_hash: H256,
|
||||||
|
/// Last block hash.
|
||||||
|
pub last_hash: RwLock<H256>,
|
||||||
|
/// Difficulty.
|
||||||
|
pub difficulty: RwLock<U256>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
/// Used for generating test client blocks.
|
||||||
|
pub enum EachBlockWith {
|
||||||
|
/// Plain block.
|
||||||
|
Nothing,
|
||||||
|
/// Block with an uncle.
|
||||||
|
Uncle,
|
||||||
|
/// Block with a transaction.
|
||||||
|
Transaction,
|
||||||
|
/// Block with an uncle and transaction.
|
||||||
|
UncleAndTransaction
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestBlockChainClient {
|
||||||
|
/// Creates new test client.
|
||||||
|
pub fn new() -> TestBlockChainClient {
|
||||||
|
|
||||||
|
let mut client = TestBlockChainClient {
|
||||||
|
blocks: RwLock::new(HashMap::new()),
|
||||||
|
numbers: RwLock::new(HashMap::new()),
|
||||||
|
genesis_hash: H256::new(),
|
||||||
|
last_hash: RwLock::new(H256::new()),
|
||||||
|
difficulty: RwLock::new(From::from(0)),
|
||||||
|
};
|
||||||
|
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
|
||||||
|
client.genesis_hash = client.last_hash.read().unwrap().clone();
|
||||||
|
client
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add blocks to test client.
|
||||||
|
pub fn add_blocks(&mut self, count: usize, with: EachBlockWith) {
|
||||||
|
let len = self.numbers.read().unwrap().len();
|
||||||
|
for n in len..(len + count) {
|
||||||
|
let mut header = BlockHeader::new();
|
||||||
|
header.difficulty = From::from(n);
|
||||||
|
header.parent_hash = self.last_hash.read().unwrap().clone();
|
||||||
|
header.number = n as BlockNumber;
|
||||||
|
let uncles = match with {
|
||||||
|
EachBlockWith::Uncle | EachBlockWith::UncleAndTransaction => {
|
||||||
|
let mut uncles = RlpStream::new_list(1);
|
||||||
|
let mut uncle_header = BlockHeader::new();
|
||||||
|
uncle_header.difficulty = From::from(n);
|
||||||
|
uncle_header.parent_hash = self.last_hash.read().unwrap().clone();
|
||||||
|
uncle_header.number = n as BlockNumber;
|
||||||
|
uncles.append(&uncle_header);
|
||||||
|
header.uncles_hash = uncles.as_raw().sha3();
|
||||||
|
uncles
|
||||||
|
},
|
||||||
|
_ => RlpStream::new_list(0)
|
||||||
|
};
|
||||||
|
let txs = match with {
|
||||||
|
EachBlockWith::Transaction | EachBlockWith::UncleAndTransaction => {
|
||||||
|
let mut txs = RlpStream::new_list(1);
|
||||||
|
let keypair = KeyPair::create().unwrap();
|
||||||
|
let tx = Transaction {
|
||||||
|
action: Action::Create,
|
||||||
|
value: U256::from(100),
|
||||||
|
data: "3331600055".from_hex().unwrap(),
|
||||||
|
gas: U256::from(100_000),
|
||||||
|
gas_price: U256::one(),
|
||||||
|
nonce: U256::zero()
|
||||||
|
};
|
||||||
|
let signed_tx = tx.sign(&keypair.secret());
|
||||||
|
txs.append(&signed_tx);
|
||||||
|
txs.out()
|
||||||
|
},
|
||||||
|
_ => rlp::NULL_RLP.to_vec()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut rlp = RlpStream::new_list(3);
|
||||||
|
rlp.append(&header);
|
||||||
|
rlp.append_raw(&txs, 1);
|
||||||
|
rlp.append_raw(uncles.as_raw(), 1);
|
||||||
|
self.import_block(rlp.as_raw().to_vec()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO:
|
||||||
|
pub fn corrupt_block(&mut self, n: BlockNumber) {
|
||||||
|
let hash = self.block_hash(BlockId::Number(n)).unwrap();
|
||||||
|
let mut header: BlockHeader = decode(&self.block_header(BlockId::Number(n)).unwrap());
|
||||||
|
header.parent_hash = H256::new();
|
||||||
|
let mut rlp = RlpStream::new_list(3);
|
||||||
|
rlp.append(&header);
|
||||||
|
rlp.append_raw(&rlp::NULL_RLP, 1);
|
||||||
|
rlp.append_raw(&rlp::NULL_RLP, 1);
|
||||||
|
self.blocks.write().unwrap().insert(hash, rlp.out());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO:
|
||||||
|
pub fn block_hash_delta_minus(&mut self, delta: usize) -> H256 {
|
||||||
|
let blocks_read = self.numbers.read().unwrap();
|
||||||
|
let index = blocks_read.len() - delta;
|
||||||
|
blocks_read[&index].clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_hash(&self, id: BlockId) -> Option<H256> {
|
||||||
|
match id {
|
||||||
|
BlockId::Hash(hash) => Some(hash),
|
||||||
|
BlockId::Number(n) => self.numbers.read().unwrap().get(&(n as usize)).cloned(),
|
||||||
|
BlockId::Earliest => self.numbers.read().unwrap().get(&0).cloned(),
|
||||||
|
BlockId::Latest => self.numbers.read().unwrap().get(&(self.numbers.read().unwrap().len() - 1)).cloned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockChainClient for TestBlockChainClient {
|
||||||
|
fn block_total_difficulty(&self, _id: BlockId) -> Option<U256> {
|
||||||
|
Some(U256::zero())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_hash(&self, _id: BlockId) -> Option<H256> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nonce(&self, _address: &Address) -> U256 {
|
||||||
|
U256::zero()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn code(&self, _address: &Address) -> Option<Bytes> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transaction(&self, _id: TransactionId) -> Option<LocalizedTransaction> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blocks_with_bloom(&self, _bloom: &H2048, _from_block: BlockId, _to_block: BlockId) -> Option<Vec<BlockNumber>> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn logs(&self, _filter: Filter) -> Vec<LocalizedLogEntry> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sealing_block(&self) -> &Mutex<Option<ClosedBlock>> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn submit_seal(&self, _pow_hash: H256, _seal: Vec<Bytes>) -> Result<(), Error> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_header(&self, id: BlockId) -> Option<Bytes> {
|
||||||
|
self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).map(|r| Rlp::new(r).at(0).as_raw().to_vec()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_body(&self, id: BlockId) -> Option<Bytes> {
|
||||||
|
self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).map(|r| {
|
||||||
|
let mut stream = RlpStream::new_list(2);
|
||||||
|
stream.append_raw(Rlp::new(&r).at(1).as_raw(), 1);
|
||||||
|
stream.append_raw(Rlp::new(&r).at(2).as_raw(), 1);
|
||||||
|
stream.out()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block(&self, id: BlockId) -> Option<Bytes> {
|
||||||
|
self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).cloned())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_status(&self, id: BlockId) -> BlockStatus {
|
||||||
|
match id {
|
||||||
|
BlockId::Number(number) if (number as usize) < self.blocks.read().unwrap().len() => BlockStatus::InChain,
|
||||||
|
BlockId::Hash(ref hash) if self.blocks.read().unwrap().get(hash).is_some() => BlockStatus::InChain,
|
||||||
|
_ => BlockStatus::Unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// works only if blocks are one after another 1 -> 2 -> 3
|
||||||
|
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute> {
|
||||||
|
Some(TreeRoute {
|
||||||
|
ancestor: H256::new(),
|
||||||
|
index: 0,
|
||||||
|
blocks: {
|
||||||
|
let numbers_read = self.numbers.read().unwrap();
|
||||||
|
let mut adding = false;
|
||||||
|
|
||||||
|
let mut blocks = Vec::new();
|
||||||
|
for (_, hash) in numbers_read.iter().sort_by(|tuple1, tuple2| tuple1.0.cmp(tuple2.0)) {
|
||||||
|
if hash == to {
|
||||||
|
if adding {
|
||||||
|
blocks.push(hash.clone());
|
||||||
|
}
|
||||||
|
adding = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if hash == from {
|
||||||
|
adding = true;
|
||||||
|
}
|
||||||
|
if adding {
|
||||||
|
blocks.push(hash.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if adding { Vec::new() } else { blocks }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: returns just hashes instead of node state rlp(?)
|
||||||
|
fn state_data(&self, hash: &H256) -> Option<Bytes> {
|
||||||
|
// starts with 'f' ?
|
||||||
|
if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") {
|
||||||
|
let mut rlp = RlpStream::new();
|
||||||
|
rlp.append(&hash.clone());
|
||||||
|
return Some(rlp.out());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_receipts(&self, hash: &H256) -> Option<Bytes> {
|
||||||
|
// starts with 'f' ?
|
||||||
|
if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") {
|
||||||
|
let receipt = Receipt::new(
|
||||||
|
H256::zero(),
|
||||||
|
U256::zero(),
|
||||||
|
vec![]);
|
||||||
|
let mut rlp = RlpStream::new();
|
||||||
|
rlp.append(&receipt);
|
||||||
|
return Some(rlp.out());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import_block(&self, b: Bytes) -> ImportResult {
|
||||||
|
let header = Rlp::new(&b).val_at::<BlockHeader>(0);
|
||||||
|
let h = header.hash();
|
||||||
|
let number: usize = header.number as usize;
|
||||||
|
if number > self.blocks.read().unwrap().len() {
|
||||||
|
panic!("Unexpected block number. Expected {}, got {}", self.blocks.read().unwrap().len(), number);
|
||||||
|
}
|
||||||
|
if number > 0 {
|
||||||
|
match self.blocks.read().unwrap().get(&header.parent_hash) {
|
||||||
|
Some(parent) => {
|
||||||
|
let parent = Rlp::new(parent).val_at::<BlockHeader>(0);
|
||||||
|
if parent.number != (header.number - 1) {
|
||||||
|
panic!("Unexpected block parent");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
panic!("Unknown block parent {:?} for block {}", header.parent_hash, number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let len = self.numbers.read().unwrap().len();
|
||||||
|
if number == len {
|
||||||
|
{
|
||||||
|
let mut difficulty = self.difficulty.write().unwrap();
|
||||||
|
*difficulty.deref_mut() = *difficulty.deref() + header.difficulty;
|
||||||
|
}
|
||||||
|
mem::replace(self.last_hash.write().unwrap().deref_mut(), h.clone());
|
||||||
|
self.blocks.write().unwrap().insert(h.clone(), b);
|
||||||
|
self.numbers.write().unwrap().insert(number, h.clone());
|
||||||
|
let mut parent_hash = header.parent_hash;
|
||||||
|
if number > 0 {
|
||||||
|
let mut n = number - 1;
|
||||||
|
while n > 0 && self.numbers.read().unwrap()[&n] != parent_hash {
|
||||||
|
*self.numbers.write().unwrap().get_mut(&n).unwrap() = parent_hash.clone();
|
||||||
|
n -= 1;
|
||||||
|
parent_hash = Rlp::new(&self.blocks.read().unwrap()[&parent_hash]).val_at::<BlockHeader>(0).parent_hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.blocks.write().unwrap().insert(h.clone(), b.to_vec());
|
||||||
|
}
|
||||||
|
Ok(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn queue_info(&self) -> BlockQueueInfo {
|
||||||
|
BlockQueueInfo {
|
||||||
|
verified_queue_size: 0,
|
||||||
|
unverified_queue_size: 0,
|
||||||
|
verifying_queue_size: 0,
|
||||||
|
max_queue_size: 0,
|
||||||
|
max_mem_use: 0,
|
||||||
|
mem_used: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_queue(&self) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn chain_info(&self) -> BlockChainInfo {
|
||||||
|
BlockChainInfo {
|
||||||
|
total_difficulty: *self.difficulty.read().unwrap(),
|
||||||
|
pending_total_difficulty: *self.difficulty.read().unwrap(),
|
||||||
|
genesis_hash: self.genesis_hash.clone(),
|
||||||
|
best_block_hash: self.last_hash.read().unwrap().clone(),
|
||||||
|
best_block_number: self.blocks.read().unwrap().len() as BlockNumber - 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -86,6 +86,7 @@ extern crate crossbeam;
|
|||||||
#[cfg(feature = "jit" )] extern crate evmjit;
|
#[cfg(feature = "jit" )] extern crate evmjit;
|
||||||
|
|
||||||
pub mod block;
|
pub mod block;
|
||||||
|
pub mod block_queue;
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod ethereum;
|
pub mod ethereum;
|
||||||
@ -119,7 +120,6 @@ mod substate;
|
|||||||
mod executive;
|
mod executive;
|
||||||
mod externalities;
|
mod externalities;
|
||||||
mod verification;
|
mod verification;
|
||||||
mod block_queue;
|
|
||||||
mod blockchain;
|
mod blockchain;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -49,10 +49,11 @@ use ethcore::spec::*;
|
|||||||
use ethcore::client::*;
|
use ethcore::client::*;
|
||||||
use ethcore::service::{ClientService, NetSyncMessage};
|
use ethcore::service::{ClientService, NetSyncMessage};
|
||||||
use ethcore::ethereum;
|
use ethcore::ethereum;
|
||||||
use ethsync::{EthSync, SyncConfig};
|
use ethsync::{EthSync, SyncConfig, SyncProvider};
|
||||||
use docopt::Docopt;
|
use docopt::Docopt;
|
||||||
use daemonize::Daemonize;
|
use daemonize::Daemonize;
|
||||||
use number_prefix::{binary_prefix, Standalone, Prefixed};
|
use number_prefix::{binary_prefix, Standalone, Prefixed};
|
||||||
|
use util::keys::store::*;
|
||||||
|
|
||||||
fn die_with_message(msg: &str) -> ! {
|
fn die_with_message(msg: &str) -> ! {
|
||||||
println!("ERROR: {}", msg);
|
println!("ERROR: {}", msg);
|
||||||
@ -79,7 +80,7 @@ Protocol Options:
|
|||||||
or olympic, frontier, homestead, mainnet, morden, or testnet [default: homestead].
|
or olympic, frontier, homestead, mainnet, morden, or testnet [default: homestead].
|
||||||
--testnet Equivalent to --chain testnet (geth-compatible).
|
--testnet Equivalent to --chain testnet (geth-compatible).
|
||||||
--networkid INDEX Override the network identifier from the chain we are on.
|
--networkid INDEX Override the network identifier from the chain we are on.
|
||||||
--archive Client should not prune the state/storage trie.
|
--pruning Client should prune the state/storage trie.
|
||||||
-d --datadir PATH Specify the database & configuration directory path [default: $HOME/.parity]
|
-d --datadir PATH Specify the database & configuration directory path [default: $HOME/.parity]
|
||||||
--keys-path PATH Specify the path for JSON key files to be found [default: $HOME/.web3/keys]
|
--keys-path PATH Specify the path for JSON key files to be found [default: $HOME/.web3/keys]
|
||||||
--identity NAME Specify your node's name.
|
--identity NAME Specify your node's name.
|
||||||
@ -140,7 +141,7 @@ struct Args {
|
|||||||
flag_identity: String,
|
flag_identity: String,
|
||||||
flag_cache: Option<usize>,
|
flag_cache: Option<usize>,
|
||||||
flag_keys_path: String,
|
flag_keys_path: String,
|
||||||
flag_archive: bool,
|
flag_pruning: bool,
|
||||||
flag_no_bootstrap: bool,
|
flag_no_bootstrap: bool,
|
||||||
flag_listen_address: String,
|
flag_listen_address: String,
|
||||||
flag_public_address: Option<String>,
|
flag_public_address: Option<String>,
|
||||||
@ -195,7 +196,7 @@ fn setup_log(init: &Option<String>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rpc")]
|
#[cfg(feature = "rpc")]
|
||||||
fn setup_rpc_server(client: Arc<Client>, sync: Arc<EthSync>, url: &str, cors_domain: &str, apis: Vec<&str>) -> Option<Arc<PanicHandler>> {
|
fn setup_rpc_server(client: Arc<Client>, sync: Arc<EthSync>, secret_store: Arc<AccountService>, url: &str, cors_domain: &str, apis: Vec<&str>) -> Option<Arc<PanicHandler>> {
|
||||||
use rpc::v1::*;
|
use rpc::v1::*;
|
||||||
|
|
||||||
let server = rpc::RpcServer::new();
|
let server = rpc::RpcServer::new();
|
||||||
@ -204,7 +205,7 @@ fn setup_rpc_server(client: Arc<Client>, sync: Arc<EthSync>, url: &str, cors_dom
|
|||||||
"web3" => server.add_delegate(Web3Client::new().to_delegate()),
|
"web3" => server.add_delegate(Web3Client::new().to_delegate()),
|
||||||
"net" => server.add_delegate(NetClient::new(&sync).to_delegate()),
|
"net" => server.add_delegate(NetClient::new(&sync).to_delegate()),
|
||||||
"eth" => {
|
"eth" => {
|
||||||
server.add_delegate(EthClient::new(&client, &sync).to_delegate());
|
server.add_delegate(EthClient::new(&client, &sync, &secret_store).to_delegate());
|
||||||
server.add_delegate(EthFilterClient::new(&client).to_delegate());
|
server.add_delegate(EthFilterClient::new(&client).to_delegate());
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
@ -402,7 +403,7 @@ impl Configuration {
|
|||||||
client_config.blockchain.max_cache_size = self.args.flag_cache_max_size;
|
client_config.blockchain.max_cache_size = self.args.flag_cache_max_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
client_config.prefer_journal = !self.args.flag_archive;
|
client_config.prefer_journal = self.args.flag_pruning;
|
||||||
client_config.name = self.args.flag_identity.clone();
|
client_config.name = self.args.flag_identity.clone();
|
||||||
client_config.queue.max_mem_use = self.args.flag_queue_max_size;
|
client_config.queue.max_mem_use = self.args.flag_queue_max_size;
|
||||||
let mut service = ClientService::start(client_config, spec, net_settings, &Path::new(&self.path())).unwrap();
|
let mut service = ClientService::start(client_config, spec, net_settings, &Path::new(&self.path())).unwrap();
|
||||||
@ -414,6 +415,9 @@ impl Configuration {
|
|||||||
// Sync
|
// Sync
|
||||||
let sync = EthSync::register(service.network(), sync_config, client);
|
let sync = EthSync::register(service.network(), sync_config, client);
|
||||||
|
|
||||||
|
// Secret Store
|
||||||
|
let account_service = Arc::new(AccountService::new());
|
||||||
|
|
||||||
// Setup rpc
|
// Setup rpc
|
||||||
if self.args.flag_jsonrpc || self.args.flag_rpc {
|
if self.args.flag_jsonrpc || self.args.flag_rpc {
|
||||||
let url = format!("{}:{}",
|
let url = format!("{}:{}",
|
||||||
@ -424,7 +428,7 @@ impl Configuration {
|
|||||||
let cors = self.args.flag_rpccorsdomain.as_ref().unwrap_or(&self.args.flag_jsonrpc_cors);
|
let cors = self.args.flag_rpccorsdomain.as_ref().unwrap_or(&self.args.flag_jsonrpc_cors);
|
||||||
// TODO: use this as the API list.
|
// TODO: use this as the API list.
|
||||||
let apis = self.args.flag_rpcapi.as_ref().unwrap_or(&self.args.flag_jsonrpc_apis);
|
let apis = self.args.flag_rpcapi.as_ref().unwrap_or(&self.args.flag_jsonrpc_apis);
|
||||||
let server_handler = setup_rpc_server(service.client(), sync.clone(), &url, cors, apis.split(",").collect());
|
let server_handler = setup_rpc_server(service.client(), sync.clone(), account_service.clone(), &url, cors, apis.split(",").collect());
|
||||||
if let Some(handler) = server_handler {
|
if let Some(handler) = server_handler {
|
||||||
panic_handler.forward_from(handler.deref());
|
panic_handler.forward_from(handler.deref());
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ impl<F, T> PollManager<F, T> where T: Timer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns number of block when last poll happend.
|
/// Returns number of block when last poll happend.
|
||||||
pub fn get_poll_info(&mut self, id: &PollId) -> Option<&PollInfo<F>> {
|
pub fn poll_info(&mut self, id: &PollId) -> Option<&PollInfo<F>> {
|
||||||
self.polls.prune();
|
self.polls.prune();
|
||||||
self.polls.get(id)
|
self.polls.get(id)
|
||||||
}
|
}
|
||||||
@ -124,21 +124,21 @@ mod tests {
|
|||||||
|
|
||||||
*time.borrow_mut() = 10;
|
*time.borrow_mut() = 10;
|
||||||
indexer.update_poll(&0, 21);
|
indexer.update_poll(&0, 21);
|
||||||
assert_eq!(indexer.get_poll_info(&0).unwrap().filter, false);
|
assert_eq!(indexer.poll_info(&0).unwrap().filter, false);
|
||||||
assert_eq!(indexer.get_poll_info(&0).unwrap().block_number, 21);
|
assert_eq!(indexer.poll_info(&0).unwrap().block_number, 21);
|
||||||
|
|
||||||
*time.borrow_mut() = 30;
|
*time.borrow_mut() = 30;
|
||||||
indexer.update_poll(&1, 23);
|
indexer.update_poll(&1, 23);
|
||||||
assert_eq!(indexer.get_poll_info(&1).unwrap().filter, true);
|
assert_eq!(indexer.poll_info(&1).unwrap().filter, true);
|
||||||
assert_eq!(indexer.get_poll_info(&1).unwrap().block_number, 23);
|
assert_eq!(indexer.poll_info(&1).unwrap().block_number, 23);
|
||||||
|
|
||||||
*time.borrow_mut() = 75;
|
*time.borrow_mut() = 75;
|
||||||
indexer.update_poll(&0, 30);
|
indexer.update_poll(&0, 30);
|
||||||
assert!(indexer.get_poll_info(&0).is_none());
|
assert!(indexer.poll_info(&0).is_none());
|
||||||
assert_eq!(indexer.get_poll_info(&1).unwrap().filter, true);
|
assert_eq!(indexer.poll_info(&1).unwrap().filter, true);
|
||||||
assert_eq!(indexer.get_poll_info(&1).unwrap().block_number, 23);
|
assert_eq!(indexer.poll_info(&1).unwrap().block_number, 23);
|
||||||
|
|
||||||
indexer.remove_poll(&1);
|
indexer.remove_poll(&1);
|
||||||
assert!(indexer.get_poll_info(&1).is_none());
|
assert!(indexer.poll_info(&1).is_none());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
//! Eth rpc implementation.
|
//! Eth rpc implementation.
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Weak, Mutex, RwLock};
|
use std::sync::{Arc, Weak, Mutex, RwLock};
|
||||||
use ethsync::{EthSync, SyncState};
|
use ethsync::{SyncProvider, SyncState};
|
||||||
use jsonrpc_core::*;
|
use jsonrpc_core::*;
|
||||||
use util::numbers::*;
|
use util::numbers::*;
|
||||||
use util::sha3::*;
|
use util::sha3::*;
|
||||||
@ -25,26 +25,28 @@ use util::rlp::encode;
|
|||||||
use ethcore::client::*;
|
use ethcore::client::*;
|
||||||
use ethcore::block::{IsBlock};
|
use ethcore::block::{IsBlock};
|
||||||
use ethcore::views::*;
|
use ethcore::views::*;
|
||||||
//#[macro_use] extern crate log;
|
|
||||||
use ethcore::ethereum::Ethash;
|
use ethcore::ethereum::Ethash;
|
||||||
use ethcore::ethereum::denominations::shannon;
|
use ethcore::ethereum::denominations::shannon;
|
||||||
use v1::traits::{Eth, EthFilter};
|
use v1::traits::{Eth, EthFilter};
|
||||||
use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, OptionalValue, Index, Filter, Log};
|
use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, TransactionRequest, OptionalValue, Index, Filter, Log};
|
||||||
use v1::helpers::{PollFilter, PollManager};
|
use v1::helpers::{PollFilter, PollManager};
|
||||||
|
use util::keys::store::AccountProvider;
|
||||||
|
|
||||||
/// Eth rpc implementation.
|
/// Eth rpc implementation.
|
||||||
pub struct EthClient {
|
pub struct EthClient<C, S, A> where C: BlockChainClient, S: SyncProvider, A: AccountProvider {
|
||||||
client: Weak<Client>,
|
client: Weak<C>,
|
||||||
sync: Weak<EthSync>,
|
sync: Weak<S>,
|
||||||
|
accounts: Weak<A>,
|
||||||
hashrates: RwLock<HashMap<H256, u64>>,
|
hashrates: RwLock<HashMap<H256, u64>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EthClient {
|
impl<C, S, A> EthClient<C, S, A> where C: BlockChainClient, S: SyncProvider, A: AccountProvider {
|
||||||
/// Creates new EthClient.
|
/// Creates new EthClient.
|
||||||
pub fn new(client: &Arc<Client>, sync: &Arc<EthSync>) -> Self {
|
pub fn new(client: &Arc<C>, sync: &Arc<S>, accounts: &Arc<A>) -> Self {
|
||||||
EthClient {
|
EthClient {
|
||||||
client: Arc::downgrade(client),
|
client: Arc::downgrade(client),
|
||||||
sync: Arc::downgrade(sync),
|
sync: Arc::downgrade(sync),
|
||||||
|
accounts: Arc::downgrade(accounts),
|
||||||
hashrates: RwLock::new(HashMap::new()),
|
hashrates: RwLock::new(HashMap::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,7 +97,7 @@ impl EthClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eth for EthClient {
|
impl<C, S, A> Eth for EthClient<C, S, A> where C: BlockChainClient + 'static, S: SyncProvider + 'static, A: AccountProvider + 'static {
|
||||||
fn protocol_version(&self, params: Params) -> Result<Value, Error> {
|
fn protocol_version(&self, params: Params) -> Result<Value, Error> {
|
||||||
match params {
|
match params {
|
||||||
Params::None => to_value(&U256::from(take_weak!(self.sync).status().protocol_version)),
|
Params::None => to_value(&U256::from(take_weak!(self.sync).status().protocol_version)),
|
||||||
@ -159,7 +161,7 @@ impl Eth for EthClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_transaction_count(&self, params: Params) -> Result<Value, Error> {
|
fn block_transaction_count_by_hash(&self, params: Params) -> Result<Value, Error> {
|
||||||
from_params::<(H256,)>(params)
|
from_params::<(H256,)>(params)
|
||||||
.and_then(|(hash,)| match take_weak!(self.client).block(BlockId::Hash(hash)) {
|
.and_then(|(hash,)| match take_weak!(self.client).block(BlockId::Hash(hash)) {
|
||||||
Some(bytes) => to_value(&BlockView::new(&bytes).transactions_count()),
|
Some(bytes) => to_value(&BlockView::new(&bytes).transactions_count()),
|
||||||
@ -167,6 +169,17 @@ impl Eth for EthClient {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn block_transaction_count_by_number(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
from_params::<(BlockNumber,)>(params)
|
||||||
|
.and_then(|(block_number,)| match block_number {
|
||||||
|
BlockNumber::Pending => to_value(&take_weak!(self.sync).status().transaction_queue_pending),
|
||||||
|
_ => match take_weak!(self.client).block(block_number.into()) {
|
||||||
|
Some(bytes) => to_value(&BlockView::new(&bytes).transactions_count()),
|
||||||
|
None => Ok(Value::Null)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn block_uncles_count(&self, params: Params) -> Result<Value, Error> {
|
fn block_uncles_count(&self, params: Params) -> Result<Value, Error> {
|
||||||
from_params::<(H256,)>(params)
|
from_params::<(H256,)>(params)
|
||||||
.and_then(|(hash,)| match take_weak!(self.client).block(BlockId::Hash(hash)) {
|
.and_then(|(hash,)| match take_weak!(self.client).block(BlockId::Hash(hash)) {
|
||||||
@ -253,17 +266,35 @@ impl Eth for EthClient {
|
|||||||
to_value(&true)
|
to_value(&true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn send_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
from_params::<(TransactionRequest, )>(params)
|
||||||
|
.and_then(|(transaction_request, )| {
|
||||||
|
let accounts = take_weak!(self.accounts);
|
||||||
|
match accounts.account_secret(&transaction_request.from) {
|
||||||
|
Ok(secret) => {
|
||||||
|
let sync = take_weak!(self.sync);
|
||||||
|
let (transaction, _) = transaction_request.to_eth();
|
||||||
|
let signed_transaction = transaction.sign(&secret);
|
||||||
|
let hash = signed_transaction.hash();
|
||||||
|
sync.insert_transaction(signed_transaction);
|
||||||
|
to_value(&hash)
|
||||||
|
},
|
||||||
|
Err(_) => { to_value(&U256::zero()) }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Eth filter rpc implementation.
|
/// Eth filter rpc implementation.
|
||||||
pub struct EthFilterClient {
|
pub struct EthFilterClient<C> where C: BlockChainClient {
|
||||||
client: Weak<Client>,
|
client: Weak<C>,
|
||||||
polls: Mutex<PollManager<PollFilter>>,
|
polls: Mutex<PollManager<PollFilter>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EthFilterClient {
|
impl<C> EthFilterClient<C> where C: BlockChainClient {
|
||||||
/// Creates new Eth filter client.
|
/// Creates new Eth filter client.
|
||||||
pub fn new(client: &Arc<Client>) -> Self {
|
pub fn new(client: &Arc<C>) -> Self {
|
||||||
EthFilterClient {
|
EthFilterClient {
|
||||||
client: Arc::downgrade(client),
|
client: Arc::downgrade(client),
|
||||||
polls: Mutex::new(PollManager::new())
|
polls: Mutex::new(PollManager::new())
|
||||||
@ -271,7 +302,7 @@ impl EthFilterClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EthFilter for EthFilterClient {
|
impl<C> EthFilter for EthFilterClient<C> where C: BlockChainClient + 'static {
|
||||||
fn new_filter(&self, params: Params) -> Result<Value, Error> {
|
fn new_filter(&self, params: Params) -> Result<Value, Error> {
|
||||||
from_params::<(Filter,)>(params)
|
from_params::<(Filter,)>(params)
|
||||||
.and_then(|(filter,)| {
|
.and_then(|(filter,)| {
|
||||||
@ -307,7 +338,7 @@ impl EthFilter for EthFilterClient {
|
|||||||
let client = take_weak!(self.client);
|
let client = take_weak!(self.client);
|
||||||
from_params::<(Index,)>(params)
|
from_params::<(Index,)>(params)
|
||||||
.and_then(|(index,)| {
|
.and_then(|(index,)| {
|
||||||
let info = self.polls.lock().unwrap().get_poll_info(&index.value()).cloned();
|
let info = self.polls.lock().unwrap().poll_info(&index.value()).cloned();
|
||||||
match info {
|
match info {
|
||||||
None => Ok(Value::Array(vec![] as Vec<Value>)),
|
None => Ok(Value::Array(vec![] as Vec<Value>)),
|
||||||
Some(info) => match info.filter {
|
Some(info) => match info.filter {
|
||||||
|
@ -17,24 +17,24 @@
|
|||||||
//! Net rpc implementation.
|
//! Net rpc implementation.
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use jsonrpc_core::*;
|
use jsonrpc_core::*;
|
||||||
use ethsync::EthSync;
|
use ethsync::SyncProvider;
|
||||||
use v1::traits::Net;
|
use v1::traits::Net;
|
||||||
|
|
||||||
/// Net rpc implementation.
|
/// Net rpc implementation.
|
||||||
pub struct NetClient {
|
pub struct NetClient<S> where S: SyncProvider {
|
||||||
sync: Weak<EthSync>
|
sync: Weak<S>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NetClient {
|
impl<S> NetClient<S> where S: SyncProvider {
|
||||||
/// Creates new NetClient.
|
/// Creates new NetClient.
|
||||||
pub fn new(sync: &Arc<EthSync>) -> Self {
|
pub fn new(sync: &Arc<S>) -> Self {
|
||||||
NetClient {
|
NetClient {
|
||||||
sync: Arc::downgrade(sync)
|
sync: Arc::downgrade(sync)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Net for NetClient {
|
impl<S> Net for NetClient<S> where S: SyncProvider + 'static {
|
||||||
fn version(&self, _: Params) -> Result<Value, Error> {
|
fn version(&self, _: Params) -> Result<Value, Error> {
|
||||||
Ok(Value::U64(take_weak!(self.sync).status().protocol_version as u64))
|
Ok(Value::U64(take_weak!(self.sync).status().protocol_version as u64))
|
||||||
}
|
}
|
||||||
|
@ -20,30 +20,28 @@ use jsonrpc_core::*;
|
|||||||
use v1::traits::Personal;
|
use v1::traits::Personal;
|
||||||
use util::keys::store::*;
|
use util::keys::store::*;
|
||||||
use util::Address;
|
use util::Address;
|
||||||
use std::sync::RwLock;
|
|
||||||
|
|
||||||
/// Account management (personal) rpc implementation.
|
/// Account management (personal) rpc implementation.
|
||||||
pub struct PersonalClient {
|
pub struct PersonalClient {
|
||||||
secret_store: Weak<RwLock<SecretStore>>,
|
accounts: Weak<AccountProvider>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PersonalClient {
|
impl PersonalClient {
|
||||||
/// Creates new PersonalClient
|
/// Creates new PersonalClient
|
||||||
pub fn new(store: &Arc<RwLock<SecretStore>>) -> Self {
|
pub fn new(store: &Arc<AccountProvider>) -> Self {
|
||||||
PersonalClient {
|
PersonalClient {
|
||||||
secret_store: Arc::downgrade(store),
|
accounts: Arc::downgrade(store),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Personal for PersonalClient {
|
impl Personal for PersonalClient {
|
||||||
fn accounts(&self, _: Params) -> Result<Value, Error> {
|
fn accounts(&self, _: Params) -> Result<Value, Error> {
|
||||||
let store_wk = take_weak!(self.secret_store);
|
let store = take_weak!(self.accounts);
|
||||||
let store = store_wk.read().unwrap();
|
|
||||||
match store.accounts() {
|
match store.accounts() {
|
||||||
Ok(account_list) => {
|
Ok(account_list) => {
|
||||||
Ok(Value::Array(account_list.iter()
|
Ok(Value::Array(account_list.iter()
|
||||||
.map(|&(account, _)| Value::String(format!("{:?}", account)))
|
.map(|&account| Value::String(format!("{:?}", account)))
|
||||||
.collect::<Vec<Value>>())
|
.collect::<Vec<Value>>())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -54,8 +52,7 @@ impl Personal for PersonalClient {
|
|||||||
fn new_account(&self, params: Params) -> Result<Value, Error> {
|
fn new_account(&self, params: Params) -> Result<Value, Error> {
|
||||||
from_params::<(String, )>(params).and_then(
|
from_params::<(String, )>(params).and_then(
|
||||||
|(pass, )| {
|
|(pass, )| {
|
||||||
let store_wk = take_weak!(self.secret_store);
|
let store = take_weak!(self.accounts);
|
||||||
let mut store = store_wk.write().unwrap();
|
|
||||||
match store.new_account(&pass) {
|
match store.new_account(&pass) {
|
||||||
Ok(address) => Ok(Value::String(format!("{:?}", address))),
|
Ok(address) => Ok(Value::String(format!("{:?}", address))),
|
||||||
Err(_) => Err(Error::internal_error())
|
Err(_) => Err(Error::internal_error())
|
||||||
@ -67,8 +64,7 @@ impl Personal for PersonalClient {
|
|||||||
fn unlock_account(&self, params: Params) -> Result<Value, Error> {
|
fn unlock_account(&self, params: Params) -> Result<Value, Error> {
|
||||||
from_params::<(Address, String, u64)>(params).and_then(
|
from_params::<(Address, String, u64)>(params).and_then(
|
||||||
|(account, account_pass, _)|{
|
|(account, account_pass, _)|{
|
||||||
let store_wk = take_weak!(self.secret_store);
|
let store = take_weak!(self.accounts);
|
||||||
let store = store_wk.read().unwrap();
|
|
||||||
match store.unlock_account(&account, &account_pass) {
|
match store.unlock_account(&account, &account_pass) {
|
||||||
Ok(_) => Ok(Value::Bool(true)),
|
Ok(_) => Ok(Value::Bool(true)),
|
||||||
Err(_) => Ok(Value::Bool(false)),
|
Err(_) => Ok(Value::Bool(false)),
|
||||||
|
@ -59,8 +59,11 @@ pub trait Eth: Sized + Send + Sync + 'static {
|
|||||||
/// Returns the number of transactions sent from given address at given time (block number).
|
/// Returns the number of transactions sent from given address at given time (block number).
|
||||||
fn transaction_count(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
fn transaction_count(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||||
|
|
||||||
/// Returns the number of transactions in a block.
|
/// Returns the number of transactions in a block given block hash.
|
||||||
fn block_transaction_count(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
fn block_transaction_count_by_hash(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||||
|
|
||||||
|
/// Returns the number of transactions in a block given block number.
|
||||||
|
fn block_transaction_count_by_number(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||||
|
|
||||||
/// Returns the number of uncles in a given block.
|
/// Returns the number of uncles in a given block.
|
||||||
fn block_uncles_count(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
fn block_uncles_count(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
|
||||||
@ -130,8 +133,8 @@ pub trait Eth: Sized + Send + Sync + 'static {
|
|||||||
delegate.add_method("eth_balance", Eth::balance);
|
delegate.add_method("eth_balance", Eth::balance);
|
||||||
delegate.add_method("eth_getStorageAt", Eth::storage_at);
|
delegate.add_method("eth_getStorageAt", Eth::storage_at);
|
||||||
delegate.add_method("eth_getTransactionCount", Eth::transaction_count);
|
delegate.add_method("eth_getTransactionCount", Eth::transaction_count);
|
||||||
delegate.add_method("eth_getBlockTransactionCountByHash", Eth::block_transaction_count);
|
delegate.add_method("eth_getBlockTransactionCountByHash", Eth::block_transaction_count_by_hash);
|
||||||
delegate.add_method("eth_getBlockTransactionCountByNumber", Eth::block_transaction_count);
|
delegate.add_method("eth_getBlockTransactionCountByNumber", Eth::block_transaction_count_by_number);
|
||||||
delegate.add_method("eth_getUncleCountByBlockHash", Eth::block_uncles_count);
|
delegate.add_method("eth_getUncleCountByBlockHash", Eth::block_uncles_count);
|
||||||
delegate.add_method("eth_getUncleCountByBlockNumber", Eth::block_uncles_count);
|
delegate.add_method("eth_getUncleCountByBlockNumber", Eth::block_uncles_count);
|
||||||
delegate.add_method("eth_code", Eth::code_at);
|
delegate.add_method("eth_code", Eth::code_at);
|
||||||
|
@ -15,7 +15,9 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use rustc_serialize::hex::ToHex;
|
use rustc_serialize::hex::ToHex;
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer, Deserialize, Deserializer, Error};
|
||||||
|
use serde::de::Visitor;
|
||||||
|
use util::common::FromHex;
|
||||||
|
|
||||||
/// Wrapper structure around vector of bytes.
|
/// Wrapper structure around vector of bytes.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -26,6 +28,7 @@ impl Bytes {
|
|||||||
pub fn new(bytes: Vec<u8>) -> Bytes {
|
pub fn new(bytes: Vec<u8>) -> Bytes {
|
||||||
Bytes(bytes)
|
Bytes(bytes)
|
||||||
}
|
}
|
||||||
|
pub fn to_vec(self) -> Vec<u8> { let Bytes(x) = self; x }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Bytes {
|
impl Default for Bytes {
|
||||||
@ -44,6 +47,32 @@ impl Serialize for Bytes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Deserialize for Bytes {
|
||||||
|
fn deserialize<D>(deserializer: &mut D) -> Result<Bytes, D::Error>
|
||||||
|
where D: Deserializer {
|
||||||
|
deserializer.deserialize(BytesVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BytesVisitor;
|
||||||
|
|
||||||
|
impl Visitor for BytesVisitor {
|
||||||
|
type Value = Bytes;
|
||||||
|
|
||||||
|
fn visit_str<E>(&mut self, value: &str) -> Result<Self::Value, E> where E: Error {
|
||||||
|
if value.len() >= 2 && &value[0..2] == "0x" {
|
||||||
|
Ok(Bytes::new(FromHex::from_hex(&value[2..]).unwrap_or_else(|_| vec![])))
|
||||||
|
} else {
|
||||||
|
Err(Error::custom("invalid hex"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_string<E>(&mut self, value: String) -> Result<Self::Value, E> where E: Error {
|
||||||
|
self.visit_str(value.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -33,3 +33,5 @@ pub use self::log::Log;
|
|||||||
pub use self::optionals::OptionalValue;
|
pub use self::optionals::OptionalValue;
|
||||||
pub use self::sync::{SyncStatus, SyncInfo};
|
pub use self::sync::{SyncStatus, SyncInfo};
|
||||||
pub use self::transaction::Transaction;
|
pub use self::transaction::Transaction;
|
||||||
|
pub use self::transaction::TransactionRequest;
|
||||||
|
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
use util::numbers::*;
|
use util::numbers::*;
|
||||||
use ethcore::transaction::{LocalizedTransaction, Action};
|
use ethcore::transaction::{LocalizedTransaction, Action};
|
||||||
use v1::types::{Bytes, OptionalValue};
|
use v1::types::{Bytes, OptionalValue};
|
||||||
|
use serde::{Deserializer, Error};
|
||||||
|
use ethcore;
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize)]
|
#[derive(Debug, Default, Serialize)]
|
||||||
pub struct Transaction {
|
pub struct Transaction {
|
||||||
@ -37,6 +39,35 @@ pub struct Transaction {
|
|||||||
pub input: Bytes
|
pub input: Bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||||
|
pub struct TransactionRequest {
|
||||||
|
pub from: Address,
|
||||||
|
pub to: Option<Address>,
|
||||||
|
#[serde(rename="gasPrice")]
|
||||||
|
pub gas_price: Option<U256>,
|
||||||
|
pub gas: Option<U256>,
|
||||||
|
pub value: Option<U256>,
|
||||||
|
pub data: Bytes,
|
||||||
|
pub nonce: Option<U256>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransactionRequest {
|
||||||
|
/// maps transaction request to the transaction that can be signed and inserted
|
||||||
|
pub fn to_eth(self) -> (ethcore::transaction::Transaction, Address) {
|
||||||
|
(ethcore::transaction::Transaction {
|
||||||
|
nonce: self.nonce.unwrap_or(U256::zero()),
|
||||||
|
action: match self.to {
|
||||||
|
None => ethcore::transaction::Action::Create,
|
||||||
|
Some(addr) => ethcore::transaction::Action::Call(addr)
|
||||||
|
},
|
||||||
|
gas: self.gas.unwrap_or(U256::zero()),
|
||||||
|
gas_price: self.gas_price.unwrap_or(U256::zero()),
|
||||||
|
value: self.value.unwrap_or(U256::zero()),
|
||||||
|
data: self.data.to_vec()
|
||||||
|
}, self.from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<LocalizedTransaction> for Transaction {
|
impl From<LocalizedTransaction> for Transaction {
|
||||||
fn from(t: LocalizedTransaction) -> Transaction {
|
fn from(t: LocalizedTransaction) -> Transaction {
|
||||||
Transaction {
|
Transaction {
|
||||||
|
@ -43,6 +43,7 @@ use io::SyncIo;
|
|||||||
use transaction_queue::TransactionQueue;
|
use transaction_queue::TransactionQueue;
|
||||||
use time;
|
use time;
|
||||||
use super::SyncConfig;
|
use super::SyncConfig;
|
||||||
|
use ethcore;
|
||||||
|
|
||||||
known_heap_size!(0, PeerInfo, Header, HeaderId);
|
known_heap_size!(0, PeerInfo, Header, HeaderId);
|
||||||
|
|
||||||
@ -140,6 +141,8 @@ pub struct SyncStatus {
|
|||||||
pub num_active_peers: usize,
|
pub num_active_peers: usize,
|
||||||
/// Heap memory used in bytes
|
/// Heap memory used in bytes
|
||||||
pub mem_used: usize,
|
pub mem_used: usize,
|
||||||
|
/// Number of pending transactions in queue
|
||||||
|
pub transaction_queue_pending: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||||
@ -255,6 +258,7 @@ impl ChainSync {
|
|||||||
blocks_total: match self.highest_block { Some(x) if x > self.starting_block => x - self.starting_block, _ => 0 },
|
blocks_total: match self.highest_block { Some(x) if x > self.starting_block => x - self.starting_block, _ => 0 },
|
||||||
num_peers: self.peers.len(),
|
num_peers: self.peers.len(),
|
||||||
num_active_peers: self.peers.values().filter(|p| p.asking != PeerAsking::Nothing).count(),
|
num_active_peers: self.peers.values().filter(|p| p.asking != PeerAsking::Nothing).count(),
|
||||||
|
transaction_queue_pending: self.transaction_queue.lock().unwrap().status().pending,
|
||||||
mem_used:
|
mem_used:
|
||||||
// TODO: https://github.com/servo/heapsize/pull/50
|
// TODO: https://github.com/servo/heapsize/pull/50
|
||||||
// self.downloading_hashes.heap_size_of_children()
|
// self.downloading_hashes.heap_size_of_children()
|
||||||
@ -1301,6 +1305,13 @@ impl ChainSync {
|
|||||||
// TODO [todr] propagate transactions?
|
// TODO [todr] propagate transactions?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add transaction to the transaction queue
|
||||||
|
pub fn insert_transaction<T>(&self, transaction: ethcore::transaction::SignedTransaction, fetch_nonce: &T)
|
||||||
|
where T: Fn(&Address) -> U256
|
||||||
|
{
|
||||||
|
let mut queue = self.transaction_queue.lock().unwrap();
|
||||||
|
queue.add(transaction, fetch_nonce);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -72,6 +72,7 @@ mod chain;
|
|||||||
mod io;
|
mod io;
|
||||||
mod range_collection;
|
mod range_collection;
|
||||||
mod transaction_queue;
|
mod transaction_queue;
|
||||||
|
pub use transaction_queue::TransactionQueue;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
@ -93,6 +94,14 @@ impl Default for SyncConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Current sync status
|
||||||
|
pub trait SyncProvider: Send + Sync {
|
||||||
|
/// Get sync status
|
||||||
|
fn status(&self) -> SyncStatus;
|
||||||
|
/// Insert transaction in the sync transaction queue
|
||||||
|
fn insert_transaction(&self, transaction: ethcore::transaction::SignedTransaction);
|
||||||
|
}
|
||||||
|
|
||||||
/// Ethereum network protocol handler
|
/// Ethereum network protocol handler
|
||||||
pub struct EthSync {
|
pub struct EthSync {
|
||||||
/// Shared blockchain client. TODO: this should evetually become an IPC endpoint
|
/// Shared blockchain client. TODO: this should evetually become an IPC endpoint
|
||||||
@ -114,11 +123,6 @@ impl EthSync {
|
|||||||
sync
|
sync
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get sync status
|
|
||||||
pub fn status(&self) -> SyncStatus {
|
|
||||||
self.sync.read().unwrap().status()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stop sync
|
/// Stop sync
|
||||||
pub fn stop(&mut self, io: &mut NetworkContext<SyncMessage>) {
|
pub fn stop(&mut self, io: &mut NetworkContext<SyncMessage>) {
|
||||||
self.sync.write().unwrap().abort(&mut NetSyncIo::new(io, self.chain.deref()));
|
self.sync.write().unwrap().abort(&mut NetSyncIo::new(io, self.chain.deref()));
|
||||||
@ -130,6 +134,22 @@ impl EthSync {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SyncProvider for EthSync {
|
||||||
|
/// Get sync status
|
||||||
|
fn status(&self) -> SyncStatus {
|
||||||
|
self.sync.read().unwrap().status()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Insert transaction in transaction queue
|
||||||
|
fn insert_transaction(&self, transaction: ethcore::transaction::SignedTransaction) {
|
||||||
|
use util::numbers::*;
|
||||||
|
|
||||||
|
let nonce_fn = |a: &Address| self.chain.state().nonce(a) + U256::one();
|
||||||
|
let sync = self.sync.write().unwrap();
|
||||||
|
sync.insert_transaction(transaction, &nonce_fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl NetworkProtocolHandler<SyncMessage> for EthSync {
|
impl NetworkProtocolHandler<SyncMessage> for EthSync {
|
||||||
fn initialize(&self, io: &NetworkContext<SyncMessage>) {
|
fn initialize(&self, io: &NetworkContext<SyncMessage>) {
|
||||||
io.register_timer(0, 1000).expect("Error registering sync timer");
|
io.register_timer(0, 1000).expect("Error registering sync timer");
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use util::*;
|
use util::*;
|
||||||
use ethcore::client::{BlockChainClient, BlockId};
|
use ethcore::client::{BlockChainClient, BlockId, EachBlockWith};
|
||||||
use io::SyncIo;
|
use io::SyncIo;
|
||||||
use chain::{SyncState};
|
use chain::{SyncState};
|
||||||
use super::helpers::*;
|
use super::helpers::*;
|
||||||
|
@ -15,300 +15,10 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use util::*;
|
use util::*;
|
||||||
use ethcore::client::{BlockChainClient, BlockStatus, TreeRoute, BlockChainInfo, TransactionId, BlockId, BlockQueueInfo};
|
use ethcore::client::{TestBlockChainClient, BlockChainClient};
|
||||||
use ethcore::header::{Header as BlockHeader, BlockNumber};
|
|
||||||
use ethcore::error::*;
|
|
||||||
use io::SyncIo;
|
use io::SyncIo;
|
||||||
use chain::ChainSync;
|
use chain::ChainSync;
|
||||||
use ::SyncConfig;
|
use ::SyncConfig;
|
||||||
use ethcore::receipt::Receipt;
|
|
||||||
use ethcore::transaction::{LocalizedTransaction, Transaction, Action};
|
|
||||||
use ethcore::filter::Filter;
|
|
||||||
use ethcore::log_entry::LocalizedLogEntry;
|
|
||||||
|
|
||||||
pub struct TestBlockChainClient {
|
|
||||||
pub blocks: RwLock<HashMap<H256, Bytes>>,
|
|
||||||
pub numbers: RwLock<HashMap<usize, H256>>,
|
|
||||||
pub genesis_hash: H256,
|
|
||||||
pub last_hash: RwLock<H256>,
|
|
||||||
pub difficulty: RwLock<U256>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum EachBlockWith {
|
|
||||||
Nothing,
|
|
||||||
Uncle,
|
|
||||||
Transaction,
|
|
||||||
UncleAndTransaction
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TestBlockChainClient {
|
|
||||||
pub fn new() -> TestBlockChainClient {
|
|
||||||
|
|
||||||
let mut client = TestBlockChainClient {
|
|
||||||
blocks: RwLock::new(HashMap::new()),
|
|
||||||
numbers: RwLock::new(HashMap::new()),
|
|
||||||
genesis_hash: H256::new(),
|
|
||||||
last_hash: RwLock::new(H256::new()),
|
|
||||||
difficulty: RwLock::new(From::from(0)),
|
|
||||||
};
|
|
||||||
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
|
|
||||||
client.genesis_hash = client.last_hash.read().unwrap().clone();
|
|
||||||
client
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_blocks(&mut self, count: usize, with: EachBlockWith) {
|
|
||||||
let len = self.numbers.read().unwrap().len();
|
|
||||||
for n in len..(len + count) {
|
|
||||||
let mut header = BlockHeader::new();
|
|
||||||
header.difficulty = From::from(n);
|
|
||||||
header.parent_hash = self.last_hash.read().unwrap().clone();
|
|
||||||
header.number = n as BlockNumber;
|
|
||||||
let uncles = match with {
|
|
||||||
EachBlockWith::Uncle | EachBlockWith::UncleAndTransaction => {
|
|
||||||
let mut uncles = RlpStream::new_list(1);
|
|
||||||
let mut uncle_header = BlockHeader::new();
|
|
||||||
uncle_header.difficulty = From::from(n);
|
|
||||||
uncle_header.parent_hash = self.last_hash.read().unwrap().clone();
|
|
||||||
uncle_header.number = n as BlockNumber;
|
|
||||||
uncles.append(&uncle_header);
|
|
||||||
header.uncles_hash = uncles.as_raw().sha3();
|
|
||||||
uncles
|
|
||||||
},
|
|
||||||
_ => RlpStream::new_list(0)
|
|
||||||
};
|
|
||||||
let txs = match with {
|
|
||||||
EachBlockWith::Transaction | EachBlockWith::UncleAndTransaction => {
|
|
||||||
let mut txs = RlpStream::new_list(1);
|
|
||||||
let keypair = KeyPair::create().unwrap();
|
|
||||||
let tx = Transaction {
|
|
||||||
action: Action::Create,
|
|
||||||
value: U256::from(100),
|
|
||||||
data: "3331600055".from_hex().unwrap(),
|
|
||||||
gas: U256::from(100_000),
|
|
||||||
gas_price: U256::one(),
|
|
||||||
nonce: U256::zero()
|
|
||||||
};
|
|
||||||
let signed_tx = tx.sign(&keypair.secret());
|
|
||||||
txs.append(&signed_tx);
|
|
||||||
txs.out()
|
|
||||||
},
|
|
||||||
_ => rlp::NULL_RLP.to_vec()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut rlp = RlpStream::new_list(3);
|
|
||||||
rlp.append(&header);
|
|
||||||
rlp.append_raw(&txs, 1);
|
|
||||||
rlp.append_raw(uncles.as_raw(), 1);
|
|
||||||
self.import_block(rlp.as_raw().to_vec()).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn corrupt_block(&mut self, n: BlockNumber) {
|
|
||||||
let hash = self.block_hash(BlockId::Number(n)).unwrap();
|
|
||||||
let mut header: BlockHeader = decode(&self.block_header(BlockId::Number(n)).unwrap());
|
|
||||||
header.parent_hash = H256::new();
|
|
||||||
let mut rlp = RlpStream::new_list(3);
|
|
||||||
rlp.append(&header);
|
|
||||||
rlp.append_raw(&rlp::NULL_RLP, 1);
|
|
||||||
rlp.append_raw(&rlp::NULL_RLP, 1);
|
|
||||||
self.blocks.write().unwrap().insert(hash, rlp.out());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn block_hash_delta_minus(&mut self, delta: usize) -> H256 {
|
|
||||||
let blocks_read = self.numbers.read().unwrap();
|
|
||||||
let index = blocks_read.len() - delta;
|
|
||||||
blocks_read[&index].clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn block_hash(&self, id: BlockId) -> Option<H256> {
|
|
||||||
match id {
|
|
||||||
BlockId::Hash(hash) => Some(hash),
|
|
||||||
BlockId::Number(n) => self.numbers.read().unwrap().get(&(n as usize)).cloned(),
|
|
||||||
BlockId::Earliest => self.numbers.read().unwrap().get(&0).cloned(),
|
|
||||||
BlockId::Latest => self.numbers.read().unwrap().get(&(self.numbers.read().unwrap().len() - 1)).cloned()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BlockChainClient for TestBlockChainClient {
|
|
||||||
fn block_total_difficulty(&self, _id: BlockId) -> Option<U256> {
|
|
||||||
Some(U256::zero())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn block_hash(&self, _id: BlockId) -> Option<H256> {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn nonce(&self, _address: &Address) -> U256 {
|
|
||||||
U256::zero()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn code(&self, _address: &Address) -> Option<Bytes> {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transaction(&self, _id: TransactionId) -> Option<LocalizedTransaction> {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn blocks_with_bloom(&self, _bloom: &H2048, _from_block: BlockId, _to_block: BlockId) -> Option<Vec<BlockNumber>> {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn logs(&self, _filter: Filter) -> Vec<LocalizedLogEntry> {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn block_header(&self, id: BlockId) -> Option<Bytes> {
|
|
||||||
self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).map(|r| Rlp::new(r).at(0).as_raw().to_vec()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn block_body(&self, id: BlockId) -> Option<Bytes> {
|
|
||||||
self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).map(|r| {
|
|
||||||
let mut stream = RlpStream::new_list(2);
|
|
||||||
stream.append_raw(Rlp::new(&r).at(1).as_raw(), 1);
|
|
||||||
stream.append_raw(Rlp::new(&r).at(2).as_raw(), 1);
|
|
||||||
stream.out()
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn block(&self, id: BlockId) -> Option<Bytes> {
|
|
||||||
self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).cloned())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn block_status(&self, id: BlockId) -> BlockStatus {
|
|
||||||
match id {
|
|
||||||
BlockId::Number(number) if (number as usize) < self.blocks.read().unwrap().len() => BlockStatus::InChain,
|
|
||||||
BlockId::Hash(ref hash) if self.blocks.read().unwrap().get(hash).is_some() => BlockStatus::InChain,
|
|
||||||
_ => BlockStatus::Unknown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// works only if blocks are one after another 1 -> 2 -> 3
|
|
||||||
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute> {
|
|
||||||
Some(TreeRoute {
|
|
||||||
ancestor: H256::new(),
|
|
||||||
index: 0,
|
|
||||||
blocks: {
|
|
||||||
let numbers_read = self.numbers.read().unwrap();
|
|
||||||
let mut adding = false;
|
|
||||||
|
|
||||||
let mut blocks = Vec::new();
|
|
||||||
for (_, hash) in numbers_read.iter().sort_by(|tuple1, tuple2| tuple1.0.cmp(tuple2.0)) {
|
|
||||||
if hash == to {
|
|
||||||
if adding {
|
|
||||||
blocks.push(hash.clone());
|
|
||||||
}
|
|
||||||
adding = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if hash == from {
|
|
||||||
adding = true;
|
|
||||||
}
|
|
||||||
if adding {
|
|
||||||
blocks.push(hash.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if adding { Vec::new() } else { blocks }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: returns just hashes instead of node state rlp(?)
|
|
||||||
fn state_data(&self, hash: &H256) -> Option<Bytes> {
|
|
||||||
// starts with 'f' ?
|
|
||||||
if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") {
|
|
||||||
let mut rlp = RlpStream::new();
|
|
||||||
rlp.append(&hash.clone());
|
|
||||||
return Some(rlp.out());
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn block_receipts(&self, hash: &H256) -> Option<Bytes> {
|
|
||||||
// starts with 'f' ?
|
|
||||||
if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") {
|
|
||||||
let receipt = Receipt::new(
|
|
||||||
H256::zero(),
|
|
||||||
U256::zero(),
|
|
||||||
vec![]);
|
|
||||||
let mut rlp = RlpStream::new();
|
|
||||||
rlp.append(&receipt);
|
|
||||||
return Some(rlp.out());
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn import_block(&self, b: Bytes) -> ImportResult {
|
|
||||||
let header = Rlp::new(&b).val_at::<BlockHeader>(0);
|
|
||||||
let h = header.hash();
|
|
||||||
let number: usize = header.number as usize;
|
|
||||||
if number > self.blocks.read().unwrap().len() {
|
|
||||||
panic!("Unexpected block number. Expected {}, got {}", self.blocks.read().unwrap().len(), number);
|
|
||||||
}
|
|
||||||
if number > 0 {
|
|
||||||
match self.blocks.read().unwrap().get(&header.parent_hash) {
|
|
||||||
Some(parent) => {
|
|
||||||
let parent = Rlp::new(parent).val_at::<BlockHeader>(0);
|
|
||||||
if parent.number != (header.number - 1) {
|
|
||||||
panic!("Unexpected block parent");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
panic!("Unknown block parent {:?} for block {}", header.parent_hash, number);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let len = self.numbers.read().unwrap().len();
|
|
||||||
if number == len {
|
|
||||||
{
|
|
||||||
let mut difficulty = self.difficulty.write().unwrap();
|
|
||||||
*difficulty.deref_mut() = *difficulty.deref() + header.difficulty;
|
|
||||||
}
|
|
||||||
mem::replace(self.last_hash.write().unwrap().deref_mut(), h.clone());
|
|
||||||
self.blocks.write().unwrap().insert(h.clone(), b);
|
|
||||||
self.numbers.write().unwrap().insert(number, h.clone());
|
|
||||||
let mut parent_hash = header.parent_hash;
|
|
||||||
if number > 0 {
|
|
||||||
let mut n = number - 1;
|
|
||||||
while n > 0 && self.numbers.read().unwrap()[&n] != parent_hash {
|
|
||||||
*self.numbers.write().unwrap().get_mut(&n).unwrap() = parent_hash.clone();
|
|
||||||
n -= 1;
|
|
||||||
parent_hash = Rlp::new(&self.blocks.read().unwrap()[&parent_hash]).val_at::<BlockHeader>(0).parent_hash;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
self.blocks.write().unwrap().insert(h.clone(), b.to_vec());
|
|
||||||
}
|
|
||||||
Ok(h)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn queue_info(&self) -> BlockQueueInfo {
|
|
||||||
BlockQueueInfo {
|
|
||||||
verified_queue_size: 0,
|
|
||||||
unverified_queue_size: 0,
|
|
||||||
verifying_queue_size: 0,
|
|
||||||
max_queue_size: 0,
|
|
||||||
max_mem_use: 0,
|
|
||||||
mem_used: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear_queue(&self) {
|
|
||||||
}
|
|
||||||
|
|
||||||
fn chain_info(&self) -> BlockChainInfo {
|
|
||||||
BlockChainInfo {
|
|
||||||
total_difficulty: *self.difficulty.read().unwrap(),
|
|
||||||
pending_total_difficulty: *self.difficulty.read().unwrap(),
|
|
||||||
genesis_hash: self.genesis_hash.clone(),
|
|
||||||
best_block_hash: self.last_hash.read().unwrap().clone(),
|
|
||||||
best_block_number: self.blocks.read().unwrap().len() as BlockNumber - 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TestIo<'p> {
|
pub struct TestIo<'p> {
|
||||||
pub chain: &'p mut TestBlockChainClient,
|
pub chain: &'p mut TestBlockChainClient,
|
||||||
|
@ -17,6 +17,67 @@
|
|||||||
// TODO [todr] - own transactions should have higher priority
|
// TODO [todr] - own transactions should have higher priority
|
||||||
|
|
||||||
//! Transaction Queue
|
//! Transaction Queue
|
||||||
|
//!
|
||||||
|
//! TransactionQueue keeps track of all transactions seen by the node (received from other peers) and own transactions
|
||||||
|
//! and orders them by priority. Top priority transactions are those with low nonce height (difference between
|
||||||
|
//! transaction's nonce and next nonce expected from this sender). If nonces are equal transaction's gas price is used
|
||||||
|
//! for comparison (higher gas price = higher priority).
|
||||||
|
//!
|
||||||
|
//! # Usage Example
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! extern crate ethcore_util as util;
|
||||||
|
//! extern crate ethcore;
|
||||||
|
//! extern crate ethsync;
|
||||||
|
//! extern crate rustc_serialize;
|
||||||
|
//!
|
||||||
|
//! use util::crypto::KeyPair;
|
||||||
|
//! use util::hash::Address;
|
||||||
|
//! use util::numbers::{Uint, U256};
|
||||||
|
//! use ethsync::TransactionQueue;
|
||||||
|
//! use ethcore::transaction::*;
|
||||||
|
//! use rustc_serialize::hex::FromHex;
|
||||||
|
//!
|
||||||
|
//! fn main() {
|
||||||
|
//! let key = KeyPair::create().unwrap();
|
||||||
|
//! let t1 = Transaction { action: Action::Create, value: U256::from(100), data: "3331600055".from_hex().unwrap(),
|
||||||
|
//! gas: U256::from(100_000), gas_price: U256::one(), nonce: U256::from(10) };
|
||||||
|
//! let t2 = Transaction { action: Action::Create, value: U256::from(100), data: "3331600055".from_hex().unwrap(),
|
||||||
|
//! gas: U256::from(100_000), gas_price: U256::one(), nonce: U256::from(11) };
|
||||||
|
//!
|
||||||
|
//! let st1 = t1.sign(&key.secret());
|
||||||
|
//! let st2 = t2.sign(&key.secret());
|
||||||
|
//! let default_nonce = |_a: &Address| U256::from(10);
|
||||||
|
//!
|
||||||
|
//! let mut txq = TransactionQueue::new();
|
||||||
|
//! txq.add(st2.clone(), &default_nonce);
|
||||||
|
//! txq.add(st1.clone(), &default_nonce);
|
||||||
|
//!
|
||||||
|
//! // Check status
|
||||||
|
//! assert_eq!(txq.status().pending, 2);
|
||||||
|
//! // Check top transactions
|
||||||
|
//! let top = txq.top_transactions(3);
|
||||||
|
//! assert_eq!(top.len(), 2);
|
||||||
|
//! assert_eq!(top[0], st1);
|
||||||
|
//! assert_eq!(top[1], st2);
|
||||||
|
//!
|
||||||
|
//! // And when transaction is removed (but nonce haven't changed)
|
||||||
|
//! // it will move invalid transactions to future
|
||||||
|
//! txq.remove(&st1.hash(), &default_nonce);
|
||||||
|
//! assert_eq!(txq.status().pending, 0);
|
||||||
|
//! assert_eq!(txq.status().future, 1);
|
||||||
|
//! assert_eq!(txq.top_transactions(3).len(), 0);
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Maintaing valid state
|
||||||
|
//!
|
||||||
|
//! 1. Whenever transaction is imported to queue (to queue) all other transactions from this sender are revalidated in current. It means that they are moved to future and back again (height recalculation & gap filling).
|
||||||
|
//! 2. Whenever transaction is removed:
|
||||||
|
//! - When it's removed from `future` - all `future` transactions heights are recalculated and then
|
||||||
|
//! we check if the transactions should go to `current` (comparing state nonce)
|
||||||
|
//! - When it's removed from `current` - all transactions from this sender (`current` & `future`) are recalculated.
|
||||||
|
//!
|
||||||
|
|
||||||
use std::cmp::{Ordering};
|
use std::cmp::{Ordering};
|
||||||
use std::collections::{HashMap, BTreeSet};
|
use std::collections::{HashMap, BTreeSet};
|
||||||
@ -28,9 +89,16 @@ use ethcore::error::Error;
|
|||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
/// Light structure used to identify transaction and it's order
|
||||||
struct TransactionOrder {
|
struct TransactionOrder {
|
||||||
|
/// Primary ordering factory. Difference between transaction nonce and expected nonce in state
|
||||||
|
/// (e.g. Tx(nonce:5), State(nonce:0) -> height: 5)
|
||||||
|
/// High nonce_height = Low priority (processed later)
|
||||||
nonce_height: U256,
|
nonce_height: U256,
|
||||||
|
/// Gas Price of the transaction.
|
||||||
|
/// Low gas price = Low priority (processed later)
|
||||||
gas_price: U256,
|
gas_price: U256,
|
||||||
|
/// Hash to identify associated transaction
|
||||||
hash: H256,
|
hash: H256,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +139,7 @@ impl Ord for TransactionOrder {
|
|||||||
let a_gas = self.gas_price;
|
let a_gas = self.gas_price;
|
||||||
let b_gas = b.gas_price;
|
let b_gas = b.gas_price;
|
||||||
if a_gas != b_gas {
|
if a_gas != b_gas {
|
||||||
return a_gas.cmp(&b_gas);
|
return b_gas.cmp(&a_gas);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare hashes
|
// Compare hashes
|
||||||
@ -79,6 +147,7 @@ impl Ord for TransactionOrder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Verified transaction (with sender)
|
||||||
struct VerifiedTransaction {
|
struct VerifiedTransaction {
|
||||||
transaction: SignedTransaction
|
transaction: SignedTransaction
|
||||||
}
|
}
|
||||||
@ -103,6 +172,11 @@ impl VerifiedTransaction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Holds transactions accessible by (address, nonce) and by priority
|
||||||
|
///
|
||||||
|
/// TransactionSet keeps number of entries below limit, but it doesn't
|
||||||
|
/// automatically happen during `insert/remove` operations.
|
||||||
|
/// You have to call `enforce_limit` to remove lowest priority transactions from set.
|
||||||
struct TransactionSet {
|
struct TransactionSet {
|
||||||
by_priority: BTreeSet<TransactionOrder>,
|
by_priority: BTreeSet<TransactionOrder>,
|
||||||
by_address: Table<Address, U256, TransactionOrder>,
|
by_address: Table<Address, U256, TransactionOrder>,
|
||||||
@ -110,11 +184,15 @@ struct TransactionSet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TransactionSet {
|
impl TransactionSet {
|
||||||
|
/// Inserts `TransactionOrder` to this set
|
||||||
fn insert(&mut self, sender: Address, nonce: U256, order: TransactionOrder) -> Option<TransactionOrder> {
|
fn insert(&mut self, sender: Address, nonce: U256, order: TransactionOrder) -> Option<TransactionOrder> {
|
||||||
self.by_priority.insert(order.clone());
|
self.by_priority.insert(order.clone());
|
||||||
self.by_address.insert(sender, nonce, order)
|
self.by_address.insert(sender, nonce, order)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove low priority transactions if there is more then specified by given `limit`.
|
||||||
|
///
|
||||||
|
/// It drops transactions from this set but also removes associated `VerifiedTransaction`.
|
||||||
fn enforce_limit(&mut self, by_hash: &mut HashMap<H256, VerifiedTransaction>) {
|
fn enforce_limit(&mut self, by_hash: &mut HashMap<H256, VerifiedTransaction>) {
|
||||||
let len = self.by_priority.len();
|
let len = self.by_priority.len();
|
||||||
if len <= self.limit {
|
if len <= self.limit {
|
||||||
@ -136,6 +214,7 @@ impl TransactionSet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Drop transaction from this set (remove from `by_priority` and `by_address`)
|
||||||
fn drop(&mut self, sender: &Address, nonce: &U256) -> Option<TransactionOrder> {
|
fn drop(&mut self, sender: &Address, nonce: &U256) -> Option<TransactionOrder> {
|
||||||
if let Some(tx_order) = self.by_address.remove(sender, nonce) {
|
if let Some(tx_order) = self.by_address.remove(sender, nonce) {
|
||||||
self.by_priority.remove(&tx_order);
|
self.by_priority.remove(&tx_order);
|
||||||
@ -144,6 +223,7 @@ impl TransactionSet {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Drop all transactions.
|
||||||
fn clear(&mut self) {
|
fn clear(&mut self) {
|
||||||
self.by_priority.clear();
|
self.by_priority.clear();
|
||||||
self.by_address.clear();
|
self.by_address.clear();
|
||||||
@ -268,6 +348,8 @@ impl TransactionQueue {
|
|||||||
// We will either move transaction to future or remove it completely
|
// We will either move transaction to future or remove it completely
|
||||||
// so there will be no transactions from this sender in current
|
// so there will be no transactions from this sender in current
|
||||||
self.last_nonces.remove(&sender);
|
self.last_nonces.remove(&sender);
|
||||||
|
// First update height of transactions in future to avoid collisions
|
||||||
|
self.update_future(&sender, current_nonce);
|
||||||
// This should move all current transactions to future and remove old transactions
|
// This should move all current transactions to future and remove old transactions
|
||||||
self.move_all_to_future(&sender, current_nonce);
|
self.move_all_to_future(&sender, current_nonce);
|
||||||
// And now lets check if there is some chain of transactions in future
|
// And now lets check if there is some chain of transactions in future
|
||||||
@ -277,6 +359,7 @@ impl TransactionQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update height of all transactions in future transactions set.
|
||||||
fn update_future(&mut self, sender: &Address, current_nonce: U256) {
|
fn update_future(&mut self, sender: &Address, current_nonce: U256) {
|
||||||
// We need to drain all transactions for current sender from future and reinsert them with updated height
|
// We need to drain all transactions for current sender from future and reinsert them with updated height
|
||||||
let all_nonces_from_sender = match self.future.by_address.row(&sender) {
|
let all_nonces_from_sender = match self.future.by_address.row(&sender) {
|
||||||
@ -285,10 +368,17 @@ impl TransactionQueue {
|
|||||||
};
|
};
|
||||||
for k in all_nonces_from_sender {
|
for k in all_nonces_from_sender {
|
||||||
let order = self.future.drop(&sender, &k).unwrap();
|
let order = self.future.drop(&sender, &k).unwrap();
|
||||||
|
if k >= current_nonce {
|
||||||
self.future.insert(sender.clone(), k, order.update_height(k, current_nonce));
|
self.future.insert(sender.clone(), k, order.update_height(k, current_nonce));
|
||||||
|
} else {
|
||||||
|
// Remove the transaction completely
|
||||||
|
self.by_hash.remove(&order.hash);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Drop all transactions from given sender from `current`.
|
||||||
|
/// Either moves them to `future` or removes them from queue completely.
|
||||||
fn move_all_to_future(&mut self, sender: &Address, current_nonce: U256) {
|
fn move_all_to_future(&mut self, sender: &Address, current_nonce: U256) {
|
||||||
let all_nonces_from_sender = match self.current.by_address.row(&sender) {
|
let all_nonces_from_sender = match self.current.by_address.row(&sender) {
|
||||||
Some(row_map) => row_map.keys().cloned().collect::<Vec<U256>>(),
|
Some(row_map) => row_map.keys().cloned().collect::<Vec<U256>>(),
|
||||||
@ -309,7 +399,7 @@ impl TransactionQueue {
|
|||||||
|
|
||||||
// Will be used when mining merged
|
// Will be used when mining merged
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
/// Returns top transactions from the queue
|
/// Returns top transactions from the queue ordered by priority.
|
||||||
pub fn top_transactions(&self, size: usize) -> Vec<SignedTransaction> {
|
pub fn top_transactions(&self, size: usize) -> Vec<SignedTransaction> {
|
||||||
self.current.by_priority
|
self.current.by_priority
|
||||||
.iter()
|
.iter()
|
||||||
@ -327,6 +417,8 @@ impl TransactionQueue {
|
|||||||
self.last_nonces.clear();
|
self.last_nonces.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if there are any transactions in `future` that should actually be promoted to `current`
|
||||||
|
/// (because nonce matches).
|
||||||
fn move_matching_future_to_current(&mut self, address: Address, mut current_nonce: U256, first_nonce: U256) {
|
fn move_matching_future_to_current(&mut self, address: Address, mut current_nonce: U256, first_nonce: U256) {
|
||||||
{
|
{
|
||||||
let by_nonce = self.future.by_address.row_mut(&address);
|
let by_nonce = self.future.by_address.row_mut(&address);
|
||||||
@ -348,6 +440,14 @@ impl TransactionQueue {
|
|||||||
self.last_nonces.insert(address, current_nonce - U256::one());
|
self.last_nonces.insert(address, current_nonce - U256::one());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds VerifiedTransaction to this queue.
|
||||||
|
///
|
||||||
|
/// Determines if it should be placed in current or future. When transaction is
|
||||||
|
/// imported to `current` also checks if there are any `future` transactions that should be promoted because of
|
||||||
|
/// this.
|
||||||
|
///
|
||||||
|
/// It ignores transactions that has already been imported (same `hash`) and replaces the transaction
|
||||||
|
/// iff `(address, nonce)` is the same but `gas_price` is higher.
|
||||||
fn import_tx<T>(&mut self, tx: VerifiedTransaction, fetch_nonce: &T)
|
fn import_tx<T>(&mut self, tx: VerifiedTransaction, fetch_nonce: &T)
|
||||||
where T: Fn(&Address) -> U256 {
|
where T: Fn(&Address) -> U256 {
|
||||||
|
|
||||||
@ -386,6 +486,10 @@ impl TransactionQueue {
|
|||||||
self.current.enforce_limit(&mut self.by_hash);
|
self.current.enforce_limit(&mut self.by_hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Replaces transaction in given set (could be `future` or `current`).
|
||||||
|
///
|
||||||
|
/// If there is already transaction with same `(sender, nonce)` it will be replaced iff `gas_price` is higher.
|
||||||
|
/// One of the transactions is dropped from set and also removed from queue entirely (from `by_hash`).
|
||||||
fn replace_transaction(tx: VerifiedTransaction, base_nonce: U256, set: &mut TransactionSet, by_hash: &mut HashMap<H256, VerifiedTransaction>) {
|
fn replace_transaction(tx: VerifiedTransaction, base_nonce: U256, set: &mut TransactionSet, by_hash: &mut HashMap<H256, VerifiedTransaction>) {
|
||||||
let order = TransactionOrder::for_transaction(&tx, base_nonce);
|
let order = TransactionOrder::for_transaction(&tx, base_nonce);
|
||||||
let hash = tx.hash();
|
let hash = tx.hash();
|
||||||
@ -571,6 +675,28 @@ mod test {
|
|||||||
assert_eq!(top[0], tx);
|
assert_eq!(top[0], tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_correctly_update_futures_when_removing() {
|
||||||
|
// given
|
||||||
|
let prev_nonce = |a: &Address| default_nonce(a) - U256::one();
|
||||||
|
let next2_nonce = |a: &Address| default_nonce(a) + U256::from(2);
|
||||||
|
|
||||||
|
let mut txq = TransactionQueue::new();
|
||||||
|
|
||||||
|
let (tx, tx2) = new_txs(U256::from(1));
|
||||||
|
txq.add(tx.clone(), &prev_nonce);
|
||||||
|
txq.add(tx2.clone(), &prev_nonce);
|
||||||
|
assert_eq!(txq.status().future, 2);
|
||||||
|
|
||||||
|
// when
|
||||||
|
txq.remove(&tx.hash(), &next2_nonce);
|
||||||
|
// should remove both transactions since they are not valid
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(txq.status().pending, 0);
|
||||||
|
assert_eq!(txq.status().future, 0);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_move_transactions_if_gap_filled() {
|
fn should_move_transactions_if_gap_filled() {
|
||||||
// given
|
// given
|
||||||
|
@ -153,7 +153,7 @@ struct UserTimer {
|
|||||||
pub struct IoManager<Message> where Message: Send + Sync {
|
pub struct IoManager<Message> where Message: Send + Sync {
|
||||||
timers: Arc<RwLock<HashMap<HandlerId, UserTimer>>>,
|
timers: Arc<RwLock<HashMap<HandlerId, UserTimer>>>,
|
||||||
handlers: Vec<Arc<IoHandler<Message>>>,
|
handlers: Vec<Arc<IoHandler<Message>>>,
|
||||||
_workers: Vec<Worker>,
|
workers: Vec<Worker>,
|
||||||
worker_channel: chase_lev::Worker<Work<Message>>,
|
worker_channel: chase_lev::Worker<Work<Message>>,
|
||||||
work_ready: Arc<Condvar>,
|
work_ready: Arc<Condvar>,
|
||||||
}
|
}
|
||||||
@ -180,7 +180,7 @@ impl<Message> IoManager<Message> where Message: Send + Sync + Clone + 'static {
|
|||||||
timers: Arc::new(RwLock::new(HashMap::new())),
|
timers: Arc::new(RwLock::new(HashMap::new())),
|
||||||
handlers: Vec::new(),
|
handlers: Vec::new(),
|
||||||
worker_channel: worker,
|
worker_channel: worker,
|
||||||
_workers: workers,
|
workers: workers,
|
||||||
work_ready: work_ready,
|
work_ready: work_ready,
|
||||||
};
|
};
|
||||||
try!(event_loop.run(&mut io));
|
try!(event_loop.run(&mut io));
|
||||||
@ -230,7 +230,10 @@ impl<Message> Handler for IoManager<Message> where Message: Send + Clone + Sync
|
|||||||
|
|
||||||
fn notify(&mut self, event_loop: &mut EventLoop<Self>, msg: Self::Message) {
|
fn notify(&mut self, event_loop: &mut EventLoop<Self>, msg: Self::Message) {
|
||||||
match msg {
|
match msg {
|
||||||
IoMessage::Shutdown => event_loop.shutdown(),
|
IoMessage::Shutdown => {
|
||||||
|
self.workers.clear();
|
||||||
|
event_loop.shutdown();
|
||||||
|
},
|
||||||
IoMessage::AddHandler { handler } => {
|
IoMessage::AddHandler { handler } => {
|
||||||
let handler_id = {
|
let handler_id = {
|
||||||
self.handlers.push(handler.clone());
|
self.handlers.push(handler.clone());
|
||||||
|
@ -78,6 +78,59 @@ struct AccountUnlock {
|
|||||||
expires: DateTime<UTC>,
|
expires: DateTime<UTC>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Basic account management trait
|
||||||
|
pub trait AccountProvider : Send + Sync {
|
||||||
|
/// Lists all accounts
|
||||||
|
fn accounts(&self) -> Result<Vec<Address>, ::std::io::Error>;
|
||||||
|
/// Unlocks account with the password provided
|
||||||
|
fn unlock_account(&self, account: &Address, pass: &str) -> Result<(), EncryptedHashMapError>;
|
||||||
|
/// Creates account
|
||||||
|
fn new_account(&self, pass: &str) -> Result<Address, ::std::io::Error>;
|
||||||
|
/// Returns secret for unlocked account
|
||||||
|
fn account_secret(&self, account: &Address) -> Result<crypto::Secret, SigningError>;
|
||||||
|
/// Returns secret for unlocked account
|
||||||
|
fn sign(&self, account: &Address, message: &H256) -> Result<crypto::Signature, SigningError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Thread-safe accounts management
|
||||||
|
pub struct AccountService {
|
||||||
|
secret_store: RwLock<SecretStore>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AccountProvider for AccountService {
|
||||||
|
/// Lists all accounts
|
||||||
|
fn accounts(&self) -> Result<Vec<Address>, ::std::io::Error> {
|
||||||
|
Ok(try!(self.secret_store.read().unwrap().accounts()).iter().map(|&(addr, _)| addr).collect::<Vec<Address>>())
|
||||||
|
}
|
||||||
|
/// Unlocks account with the password provided
|
||||||
|
fn unlock_account(&self, account: &Address, pass: &str) -> Result<(), EncryptedHashMapError> {
|
||||||
|
self.secret_store.read().unwrap().unlock_account(account, pass)
|
||||||
|
}
|
||||||
|
/// Creates account
|
||||||
|
fn new_account(&self, pass: &str) -> Result<Address, ::std::io::Error> {
|
||||||
|
self.secret_store.write().unwrap().new_account(pass)
|
||||||
|
}
|
||||||
|
/// Returns secret for unlocked account
|
||||||
|
fn account_secret(&self, account: &Address) -> Result<crypto::Secret, SigningError> {
|
||||||
|
self.secret_store.read().unwrap().account_secret(account)
|
||||||
|
}
|
||||||
|
/// Returns secret for unlocked account
|
||||||
|
fn sign(&self, account: &Address, message: &H256) -> Result<crypto::Signature, SigningError> {
|
||||||
|
self.secret_store.read().unwrap().sign(account, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AccountService {
|
||||||
|
/// New account service with the default location
|
||||||
|
pub fn new() -> AccountService {
|
||||||
|
let secret_store = RwLock::new(SecretStore::new());
|
||||||
|
secret_store.write().unwrap().try_import_existing();
|
||||||
|
AccountService {
|
||||||
|
secret_store: secret_store
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SecretStore {
|
impl SecretStore {
|
||||||
/// new instance of Secret Store in default home directory
|
/// new instance of Secret Store in default home directory
|
||||||
pub fn new() -> SecretStore {
|
pub fn new() -> SecretStore {
|
||||||
|
Loading…
Reference in New Issue
Block a user