Non-functioning draft of code.

This commit is contained in:
Gav Wood 2016-03-22 13:05:18 +01:00
parent 6701aff2a2
commit a134f939e9
4 changed files with 134 additions and 37 deletions

View File

@ -75,7 +75,7 @@ impl Decodable for Block {
}
/// Internal type for a block's common elements.
#[derive(Debug)]
#[derive(Clone)]
pub struct ExecutedBlock {
base: Block,
@ -168,9 +168,11 @@ pub struct OpenBlock<'x> {
/// and collected the uncles.
///
/// There is no function available to push a transaction.
#[derive(Clone)]
pub struct ClosedBlock {
block: ExecutedBlock,
uncle_bytes: Bytes,
last_hashes: LastHashes,
}
/// A block that has a valid seal.
@ -290,6 +292,7 @@ impl<'x> OpenBlock<'x> {
ClosedBlock {
block: s.block,
uncle_bytes: uncle_bytes,
last_hashes: s.last_hashes,
}
}
}
@ -332,6 +335,15 @@ impl ClosedBlock {
/// Drop this object and return the underlieing database.
pub fn drain(self) -> Box<JournalDB> { self.block.state.drop().1 }
/// Given an engine reference, reopen the `ClosedBlock` into an `OpenBlock`.
pub fn reopen(self, engine: &Engine) -> OpenBlock {
OpenBlock {
block: self.block,
engine: engine,
last_hashes: self.last_hashes,
}
}
}
impl SealedBlock {

View File

@ -339,6 +339,18 @@ impl fmt::Debug for State {
}
}
impl Clone for State {
fn clone(&self) -> State {
State {
db: self.db.spawn(),
root: self.root.clone(),
cache: RefCell::new(self.cache.borrow().clone()),
snapshots: RefCell::new(self.snapshots.borrow().clone()),
account_start_nonce: self.account_start_nonce.clone(),
}
}
}
#[cfg(test)]
mod tests {

View File

@ -28,6 +28,72 @@ use ethcore::error::{Error};
use ethcore::transaction::SignedTransaction;
use super::{MinerService, MinerStatus, TransactionQueue, AccountDetails};
struct SealingWork {
/// Not yet being sealed by a miner, but if one asks for work, we'd prefer they do this.
would_seal: Option<ClosedBlock>,
/// Currently being sealed by miners.
being_sealed: Vec<ClosedBlock>,
}
impl SealingWork {
// inspect the work that would be given.
fn pending_ref(&self) -> Option<&ClosedBlock> {
self.would_seal.as_ref().or(self.being_sealed.last().as_ref())
}
// return the a reference to forst block that returns true to `f`.
fn find_if<F>(&self, f: F) -> Option<&ClosedBlock> where F: Fn(&ClosedBlock) -> bool {
if would_seal.as_ref().map(&f).unwrap_or(false) {
would_seal.as_ref()
} else {
being_sealed.iter().find_if(f)
}
}
// used for getting the work to be done.
fn use_pending_ref(&mut self) -> Option<&ClosedBlock> {
if let Some(x) = self.would_seal.take() {
self.being_sealed.push(x);
if self.being_sealed.len() > MAX_SEALING_BLOCKS_CACHE {
self.being_sealed.erase(0);
}
}
self.being_sealed.last().as_ref()
}
// set new work to be done.
fn set_pending(&mut self, b: ClosedBlock) {
self.would_seal = Some(b);
}
// get the pending block if `f(pending)`. if there is no pending block or it doesn't pass `f`, None.
// will not destroy a block if a reference to it has previously been returned by `use_pending_ref`.
fn pending_if<F>(&self, f: F) -> Option<ClosedBlock> where F: Fn(&ClosedBlock) -> bool {
// a bit clumsy - TODO: think about a nicer way of expressing this.
if let Some(x) = self.would_seal.take() {
if f(&x) {
Some(x)
} else {
self.would_seal = x;
None
}
} else {
being_sealed.last().as_ref().filter(&b).map(|b| b.clone())
/* being_sealed.last().as_ref().and_then(|b| if f(b) {
Some(b.clone())
} else {
None
})*/
}
}
// clears everything.
fn reset(&mut self) {
self.would_seal = None;
self.being_sealed.clear();
}
}
/// Keeps track of transactions using priority queue and holds currently mined block.
pub struct Miner {
transaction_queue: Mutex<TransactionQueue>,
@ -35,20 +101,33 @@ pub struct Miner {
// for sealing...
sealing_enabled: AtomicBool,
sealing_block_last_request: Mutex<u64>,
sealing_block: Mutex<Option<ClosedBlock>>,
sealing_work: Mutex<SealingWork>,
gas_floor_target: RwLock<U256>,
author: RwLock<Address>,
extra_data: RwLock<Bytes>,
}
/*
let sealing_work = self.sealing_work.lock();
// TODO: check to see if last ClosedBlock in would_seals is same.
// if so, duplicate, re-open and push any new transactions.
// if at least one was pushed successfully, close and enqueue new ClosedBlock;
// and remove first ClosedBlock from the queue..
*/
impl Default for Miner {
fn default() -> Miner {
Miner {
transaction_queue: Mutex::new(TransactionQueue::new()),
sealing_enabled: AtomicBool::new(false),
sealing_block_last_request: Mutex::new(0),
sealing_block: Mutex::new(None),
sealing_work: Mutex::new(SealingWork{
would_seal: None,
being_sealed: vec![],
}),
gas_floor_target: RwLock::new(U256::zero()),
author: RwLock::new(Address::default()),
extra_data: RwLock::new(Vec::new()),
@ -107,7 +186,7 @@ impl Miner {
transactions,
);
*self.sealing_block.lock().unwrap() = b.map(|(block, invalid_transactions)| {
if let Some((block, invalid_transactions)) = b {
let mut queue = self.transaction_queue.lock().unwrap();
queue.remove_all(
&invalid_transactions.into_iter().collect::<Vec<H256>>(),
@ -116,8 +195,8 @@ impl Miner {
balance: chain.balance(a),
}
);
block
});
self.sealing_work.lock().unwrap().set_pending(block);
}
}
fn update_gas_limit(&self, chain: &BlockChainClient) {
@ -138,11 +217,11 @@ impl MinerService for Miner {
fn status(&self) -> MinerStatus {
let status = self.transaction_queue.lock().unwrap().status();
let block = self.sealing_block.lock().unwrap();
let sealing_work = self.sealing_work.lock().unwrap();
MinerStatus {
transactions_in_pending_queue: status.pending,
transactions_in_future_queue: status.future,
transactions_in_pending_block: block.as_ref().map_or(0, |b| b.transactions().len()),
transactions_in_pending_block: block.pending_ref().map_or(0, |b| b.transactions().len()),
}
}
@ -167,40 +246,36 @@ impl MinerService for Miner {
if should_disable_sealing {
self.sealing_enabled.store(false, atomic::Ordering::Relaxed);
*self.sealing_block.lock().unwrap() = None;
*self.sealing_work.lock().unwrap().reset();
} else if self.sealing_enabled.load(atomic::Ordering::Relaxed) {
self.prepare_sealing(chain);
}
}
fn sealing_block(&self, chain: &BlockChainClient) -> &Mutex<Option<ClosedBlock>> {
if self.sealing_block.lock().unwrap().is_none() {
fn map_sealing_work<F, T>(&self, chain: &BlockChainClient, f: F) -> Option<T> where F: FnOnce(&ClosedBlock) -> T {
let have_work = self.sealing_work.lock().unwrap().pending_ref().is_none();
if !have_work {
self.sealing_enabled.store(true, atomic::Ordering::Relaxed);
self.prepare_sealing(chain);
}
*self.sealing_block_last_request.lock().unwrap() = chain.chain_info().best_block_number;
&self.sealing_block
self.sealing_work.lock().unwrap().use_pending().map(f)
}
fn submit_seal(&self, chain: &BlockChainClient, 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 chain.try_seal(b.unwrap(), 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!(chain.import_block(sealed.rlp_bytes()));
Ok(())
if let Some(b) = self.sealing_work().lock().unwrap().take_if(|b| &b.hash() == &pow_hash) {
match chain.try_seal(b.unwrap(), seal) {
Err(old) => {
Err(Error::PowInvalid)
}
Ok(sealed) => {
// TODO: commit DB from `sealed.drain` and make a VerifiedBlock to skip running the transactions twice.
try!(chain.import_block(sealed.rlp_bytes()));
Ok(())
}
}
} else {
Err(Error::PowHashInvalid)
}
}
@ -281,7 +356,7 @@ mod tests {
let miner = Miner::default();
// when
let res = miner.sealing_block(&client);
let res = miner.would_seal(&client);
// then
assert!(res.lock().unwrap().is_some(), "Expected closed block");
@ -292,7 +367,7 @@ mod tests {
// given
let client = TestBlockChainClient::default();
let miner = Miner::default();
let res = miner.sealing_block(&client);
let res = miner.would_seal(&client);
// TODO [ToDr] Uncomment after fixing TestBlockChainClient
// assert!(res.lock().unwrap().is_some(), "Expected closed block");

View File

@ -334,7 +334,7 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
match params {
Params::None => {
let client = take_weak!(self.client);
// check if we're still syncing and return empty strings int that case
// check if we're still syncing and return empty strings in that case
{
let sync = take_weak!(self.sync);
if sync.status().state != SyncState::Idle && client.queue_info().is_empty() {
@ -343,17 +343,15 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
}
let miner = take_weak!(self.miner);
let client = take_weak!(self.client);
let u = miner.sealing_block(client.deref()).lock().unwrap();
match *u {
Some(ref b) => {
miner.map_sealing_work(client.deref(), |b| match b {
Some(b) => {
let pow_hash = b.hash();
let target = Ethash::difficulty_to_boundary(b.block().header().difficulty());
let seed_hash = Ethash::get_seedhash(b.block().header().number());
to_value(&(pow_hash, seed_hash, target))
}
_ => Err(Error::internal_error())
}
})
},
_ => Err(Error::invalid_params())
}