Reorganised ImportError to be a type of Errpr (rather than vice-versa).

Added support for eth_submitWork.
This commit is contained in:
Gav Wood 2016-03-01 00:02:48 +01:00
parent ffc5c2ea7b
commit 394e9c679b
9 changed files with 102 additions and 56 deletions

View File

@ -21,7 +21,7 @@
use common::*; use common::*;
use engine::*; use engine::*;
use state::*; use state::*;
use verification::PreVerifiedBlock; use verification::PreverifiedBlock;
/// A block, encoded as it is on the block chain. /// A block, encoded as it is on the block chain.
// TODO: rename to Block // TODO: rename to Block
@ -302,6 +302,18 @@ impl ClosedBlock {
Ok(SealedBlock { block: s.block, uncle_bytes: s.uncle_bytes }) Ok(SealedBlock { block: s.block, uncle_bytes: s.uncle_bytes })
} }
/// Provide a valid seal in order to turn this into a `SealedBlock`.
/// This does check the validity of `seal` with the engine.
/// Returns the `ClosedBlock` back again if the seal is no good.
pub fn try_seal(self, engine: &Engine, seal: Vec<Bytes>) -> Result<SealedBlock, ClosedBlock> {
let mut s = self;
s.block.base.header.set_seal(seal);
match engine.verify_block_basic(&s.block.base.header, None).is_err() || engine.verify_block_unordered(&s.block.base.header, None).is_err() {
false => Err(s),
true => Ok(SealedBlock { block: s.block, uncle_bytes: s.uncle_bytes }),
}
}
/// Drop this object and return the underlieing database. /// Drop this object and return the underlieing database.
pub fn drain(self) -> JournalDB { self.block.state.drop().1 } pub fn drain(self) -> JournalDB { self.block.state.drop().1 }
} }
@ -350,7 +362,7 @@ pub fn enact_bytes(block_bytes: &[u8], engine: &Engine, db: JournalDB, parent: &
} }
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
pub fn enact_verified(block: &PreVerifiedBlock, engine: &Engine, db: JournalDB, parent: &Header, last_hashes: LastHashes) -> Result<ClosedBlock, Error> { pub fn enact_verified(block: &PreverifiedBlock, engine: &Engine, db: JournalDB, parent: &Header, last_hashes: LastHashes) -> Result<ClosedBlock, Error> {
let view = BlockView::new(&block.bytes); let view = BlockView::new(&block.bytes);
enact(&block.header, &block.transactions, &view.uncles(), engine, db, parent, last_hashes) enact(&block.header, &block.transactions, &view.uncles(), engine, db, parent, last_hashes)
} }

View File

@ -28,7 +28,7 @@ use service::*;
use client::BlockStatus; use client::BlockStatus;
use util::panics::*; use util::panics::*;
known_heap_size!(0, UnVerifiedBlock, VerifyingBlock, PreVerifiedBlock); known_heap_size!(0, UnverifiedBlock, VerifyingBlock, PreverifiedBlock);
const MIN_MEM_LIMIT: usize = 16384; const MIN_MEM_LIMIT: usize = 16384;
const MIN_QUEUE_LIMIT: usize = 512; const MIN_QUEUE_LIMIT: usize = 512;
@ -105,14 +105,14 @@ pub struct BlockQueue {
max_mem_use: usize, max_mem_use: usize,
} }
struct UnVerifiedBlock { struct UnverifiedBlock {
header: Header, header: Header,
bytes: Bytes, bytes: Bytes,
} }
struct VerifyingBlock { struct VerifyingBlock {
hash: H256, hash: H256,
block: Option<PreVerifiedBlock>, block: Option<PreverifiedBlock>,
} }
struct QueueSignal { struct QueueSignal {
@ -134,8 +134,8 @@ impl QueueSignal {
#[derive(Default)] #[derive(Default)]
struct Verification { struct Verification {
unverified: VecDeque<UnVerifiedBlock>, unverified: VecDeque<UnverifiedBlock>,
verified: VecDeque<PreVerifiedBlock>, verified: VecDeque<PreverifiedBlock>,
verifying: VecDeque<VerifyingBlock>, verifying: VecDeque<VerifyingBlock>,
bad: HashSet<H256>, bad: HashSet<H256>,
} }
@ -244,7 +244,7 @@ impl BlockQueue {
} }
} }
fn drain_verifying(verifying: &mut VecDeque<VerifyingBlock>, verified: &mut VecDeque<PreVerifiedBlock>, bad: &mut HashSet<H256>) { fn drain_verifying(verifying: &mut VecDeque<VerifyingBlock>, verified: &mut VecDeque<PreverifiedBlock>, bad: &mut HashSet<H256>) {
while !verifying.is_empty() && verifying.front().unwrap().block.is_some() { while !verifying.is_empty() && verifying.front().unwrap().block.is_some() {
let block = verifying.pop_front().unwrap().block.unwrap(); let block = verifying.pop_front().unwrap().block.unwrap();
if bad.contains(&block.header.parent_hash) { if bad.contains(&block.header.parent_hash) {
@ -289,31 +289,31 @@ impl BlockQueue {
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(ImportError::AlreadyQueued); return Err(x!(ImportError::AlreadyQueued));
} }
{ {
let mut verification = self.verification.lock().unwrap(); let mut verification = self.verification.lock().unwrap();
if verification.bad.contains(&h) { if verification.bad.contains(&h) {
return Err(ImportError::Bad(None)); return Err(x!(ImportError::KnownBad));
} }
if verification.bad.contains(&header.parent_hash) { if verification.bad.contains(&header.parent_hash) {
verification.bad.insert(h.clone()); verification.bad.insert(h.clone());
return Err(ImportError::Bad(None)); return Err(x!(ImportError::KnownBad));
} }
} }
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.lock().unwrap().unverified.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.lock().unwrap().bad.insert(h.clone());
Err(From::from(err)) Err(err)
} }
} }
} }
@ -352,7 +352,7 @@ 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(&mut self, max: usize) -> Vec<PreverifiedBlock> {
let mut verification = self.verification.lock().unwrap(); let mut verification = self.verification.lock().unwrap();
let count = min(max, verification.verified.len()); let count = min(max, verification.verified.len());
let mut result = Vec::with_capacity(count); let mut result = Vec::with_capacity(count);

View File

@ -176,7 +176,7 @@ pub struct ClientReport {
impl ClientReport { impl ClientReport {
/// Alter internal reporting to reflect the additional `block` has been processed. /// Alter internal reporting to reflect the additional `block` has been processed.
pub fn accrue_block(&mut self, block: &PreVerifiedBlock) { pub fn accrue_block(&mut self, block: &PreverifiedBlock) {
self.blocks_imported += 1; self.blocks_imported += 1;
self.transactions_applied += block.transactions.len(); self.transactions_applied += block.transactions.len();
self.gas_processed = self.gas_processed + block.header.gas_used; self.gas_processed = self.gas_processed + block.header.gas_used;
@ -257,7 +257,7 @@ impl Client {
last_hashes last_hashes
} }
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;
@ -433,6 +433,28 @@ impl Client {
/// Grab the `ClosedBlock` that we want to be sealed. Comes as a mutex that you have to lock. /// 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>> { &self.sealing_block } pub fn sealing_block(&self) -> &Mutex<Option<ClosedBlock>> { &self.sealing_block }
/// Submit `seal` as a valid solution for the header of `pow_hash`.
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
@ -509,12 +531,14 @@ impl BlockChainClient for Client {
} }
fn import_block(&self, bytes: Bytes) -> ImportResult { fn import_block(&self, bytes: Bytes) -> ImportResult {
let header = BlockView::new(&bytes).header(); {
if self.chain.read().unwrap().is_known(&header.hash()) { let header = BlockView::new(&bytes).header_view();
return Err(ImportError::AlreadyInChain); if self.chain.read().unwrap().is_known(&header.sha3()) {
} return Err(x!(ImportError::AlreadyInChain));
if self.block_status(BlockId::Hash(header.parent_hash)) == BlockStatus::Unknown { }
return Err(ImportError::UnknownParent); if self.block_status(BlockId::Hash(header.parent_hash())) == BlockStatus::Unknown {
return Err(x!(BlockError::UnknownParent(header.parent_hash())));
}
} }
self.block_queue.write().unwrap().import_block(bytes) self.block_queue.write().unwrap().import_block(bytes)
} }

View File

@ -30,8 +30,6 @@ pub trait Engine : Sync + Send {
/// The number of additional header fields required for this engine. /// The number of additional header fields required for this engine.
fn seal_fields(&self) -> usize { 0 } fn seal_fields(&self) -> usize { 0 }
/// Default values of the additional fields RLP-encoded in a raw (non-list) harness.
fn seal_rlp(&self) -> Bytes { vec![] }
/// Additional engine-specific information for the user/developer concerning `header`. /// Additional engine-specific information for the user/developer concerning `header`.
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() } fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() }

View File

@ -131,25 +131,14 @@ pub enum BlockError {
#[derive(Debug)] #[derive(Debug)]
/// Import to the block queue result /// Import to the block queue result
pub enum ImportError { pub enum ImportError {
/// Bad block detected /// Already in the block chain.
Bad(Option<Error>),
/// Already in the block chain
AlreadyInChain, AlreadyInChain,
/// Already in the block queue /// Already in the block queue.
AlreadyQueued, AlreadyQueued,
/// Unknown parent /// Already marked as bad from a previous import (could mean parent is bad).
UnknownParent, KnownBad,
} }
impl From<Error> for ImportError {
fn from(err: Error) -> ImportError {
ImportError::Bad(Some(err))
}
}
/// Result of import block operation.
pub type ImportResult = Result<H256, ImportError>;
#[derive(Debug)] #[derive(Debug)]
/// General error type which should be capable of representing all errors in ethcore. /// General error type which should be capable of representing all errors in ethcore.
pub enum Error { pub enum Error {
@ -163,14 +152,29 @@ pub enum Error {
Execution(ExecutionError), Execution(ExecutionError),
/// Error concerning transaction processing. /// Error concerning transaction processing.
Transaction(TransactionError), Transaction(TransactionError),
/// Error concerning block import.
Import(ImportError),
/// PoW hash is invalid or out of date.
PowHashInvalid,
/// The value of the nonce or mishash is invalid.
PowInvalid,
} }
/// Result of import block operation.
pub type ImportResult = Result<H256, Error>;
impl From<TransactionError> for Error { impl From<TransactionError> for Error {
fn from(err: TransactionError) -> Error { fn from(err: TransactionError) -> Error {
Error::Transaction(err) Error::Transaction(err)
} }
} }
impl From<ImportError> for Error {
fn from(err: ImportError) -> Error {
Error::Import(err)
}
}
impl From<BlockError> for Error { impl From<BlockError> for Error {
fn from(err: BlockError) -> Error { fn from(err: BlockError) -> Error {
Error::Block(err) Error::Block(err)

View File

@ -74,8 +74,6 @@ impl Engine for Ethash {
fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) } fn version(&self) -> SemanticVersion { SemanticVersion::new(1, 0, 0) }
// Two fields - mix // Two fields - mix
fn seal_fields(&self) -> usize { 2 } fn seal_fields(&self) -> usize { 2 }
// Two empty data items in RLP.
fn seal_rlp(&self) -> Bytes { encode(&H64::new()).to_vec() }
/// Additional engine-specific information for the user/developer concerning `header`. /// Additional engine-specific information for the user/developer concerning `header`.
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() } fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() }
@ -261,12 +259,15 @@ impl Ethash {
} }
impl Header { impl Header {
fn nonce(&self) -> H64 { pub fn nonce(&self) -> H64 {
decode(&self.seal()[1]) decode(&self.seal()[1])
} }
fn mix_hash(&self) -> H256 { pub fn mix_hash(&self) -> H256 {
decode(&self.seal()[0]) decode(&self.seal()[0])
} }
pub fn set_nonce_and_mix_hash(&mut self, nonce: &H64, mix_hash: &H256) {
self.seal = vec![encode(mix_hash).to_vec(), encode(nonce).to_vec()];
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -26,7 +26,7 @@ use engine::Engine;
use blockchain::*; use blockchain::*;
/// Preprocessed block data gathered in `verify_block_unordered` call /// Preprocessed block data gathered in `verify_block_unordered` call
pub struct PreVerifiedBlock { pub struct PreverifiedBlock {
/// Populated block header /// Populated block header
pub header: Header, pub header: Header,
/// Populated block transactions /// Populated block transactions
@ -55,8 +55,8 @@ pub fn verify_block_basic(header: &Header, bytes: &[u8], engine: &Engine) -> Res
/// Phase 2 verification. Perform costly checks such as transaction signatures and block nonce for ethash. /// Phase 2 verification. Perform costly checks such as transaction signatures and block nonce for ethash.
/// Still operates on a individual block /// Still operates on a individual block
/// Returns a PreVerifiedBlock structure populated with transactions /// Returns a PreverifiedBlock structure populated with transactions
pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine) -> Result<PreVerifiedBlock, Error> { pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine) -> Result<PreverifiedBlock, Error> {
try!(engine.verify_block_unordered(&header, Some(&bytes))); try!(engine.verify_block_unordered(&header, Some(&bytes)));
for u in Rlp::new(&bytes).at(2).iter().map(|rlp| rlp.as_val::<Header>()) { for u in Rlp::new(&bytes).at(2).iter().map(|rlp| rlp.as_val::<Header>()) {
try!(engine.verify_block_unordered(&u, None)); try!(engine.verify_block_unordered(&u, None));
@ -70,7 +70,7 @@ pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine) ->
transactions.push(t); transactions.push(t);
} }
} }
Ok(PreVerifiedBlock { Ok(PreverifiedBlock {
header: header, header: header,
transactions: transactions, transactions: transactions,
bytes: bytes, bytes: bytes,

View File

@ -21,6 +21,7 @@ use jsonrpc_core::*;
use util::hash::*; use util::hash::*;
use util::uint::*; use util::uint::*;
use util::sha3::*; use util::sha3::*;
use util::rlp::encode;
use ethcore::client::*; use ethcore::client::*;
use ethcore::block::{IsBlock}; use ethcore::block::{IsBlock};
use ethcore::views::*; use ethcore::views::*;
@ -221,10 +222,10 @@ impl Eth for EthClient {
let u = c.sealing_block().lock().unwrap(); let u = c.sealing_block().lock().unwrap();
match *u { match *u {
Some(ref b) => { Some(ref b) => {
let current_hash = b.hash(); let pow_hash = b.hash();
let target = Ethash::difficulty_to_boundary(b.block().header().difficulty()); let target = Ethash::difficulty_to_boundary(b.block().header().difficulty());
let seed_hash = get_seedhash(b.block().header().number()); let seed_hash = get_seedhash(b.block().header().number());
to_value(&(current_hash, seed_hash, target)) to_value(&(pow_hash, seed_hash, target))
} }
_ => Err(Error::invalid_params()) _ => Err(Error::invalid_params())
} }
@ -233,7 +234,13 @@ impl Eth for EthClient {
} }
} }
// fn submit_work(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() } fn submit_work(&self, params: Params) -> Result<Value, Error> {
from_params::<(H64, H256, H256)>(params).and_then(|(nonce, pow_hash, mix_hash)| {
let c = take_weak!(self.client);
let seal = vec![encode(&mix_hash).to_vec(), encode(&nonce).to_vec()];
to_value(&c.submit_seal(pow_hash, seal).is_ok())
})
}
// fn submit_hashrate(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() } // fn submit_hashrate(&self, _: Params) -> Result<Value, Error> { rpc_unimplemented!() }
} }

View File

@ -477,19 +477,19 @@ impl ChainSync {
// TODO: Decompose block and add to self.headers and self.bodies instead // TODO: Decompose block and add to self.headers and self.bodies instead
if header.number == From::from(self.current_base_block() + 1) { if header.number == From::from(self.current_base_block() + 1) {
match io.chain().import_block(block_rlp.as_raw().to_vec()) { match io.chain().import_block(block_rlp.as_raw().to_vec()) {
Err(ImportError::AlreadyInChain) => { Err(Error::Import(ImportError::AlreadyInChain)) => {
trace!(target: "sync", "New block already in chain {:?}", h); trace!(target: "sync", "New block already in chain {:?}", h);
}, },
Err(ImportError::AlreadyQueued) => { Err(Error::Import(ImportError::AlreadyQueued)) => {
trace!(target: "sync", "New block already queued {:?}", h); trace!(target: "sync", "New block already queued {:?}", h);
}, },
Ok(_) => { Ok(_) => {
self.last_imported_block = Some(header.number); self.last_imported_block = Some(header.number);
trace!(target: "sync", "New block queued {:?}", h); trace!(target: "sync", "New block queued {:?}", h);
}, },
Err(ImportError::UnknownParent) => { Err(Error::Block(BlockError::UnknownParent(p))) => {
unknown = true; unknown = true;
trace!(target: "sync", "New block with unknown parent {:?}", h); trace!(target: "sync", "New block with unknown parent ({:?}) {:?}", p, h);
}, },
Err(e) => { Err(e) => {
debug!(target: "sync", "Bad new block {:?} : {:?}", h, e); debug!(target: "sync", "Bad new block {:?} : {:?}", h, e);
@ -781,12 +781,12 @@ impl ChainSync {
} }
match io.chain().import_block(block_rlp.out()) { match io.chain().import_block(block_rlp.out()) {
Err(ImportError::AlreadyInChain) => { Err(Error::Import(ImportError::AlreadyInChain)) => {
trace!(target: "sync", "Block already in chain {:?}", h); trace!(target: "sync", "Block already in chain {:?}", h);
self.last_imported_block = Some(headers.0 + i as BlockNumber); self.last_imported_block = Some(headers.0 + i as BlockNumber);
self.last_imported_hash = Some(h.clone()); self.last_imported_hash = Some(h.clone());
}, },
Err(ImportError::AlreadyQueued) => { Err(Error::Import(ImportError::AlreadyQueued)) => {
trace!(target: "sync", "Block already queued {:?}", h); trace!(target: "sync", "Block already queued {:?}", h);
self.last_imported_block = Some(headers.0 + i as BlockNumber); self.last_imported_block = Some(headers.0 + i as BlockNumber);
self.last_imported_hash = Some(h.clone()); self.last_imported_hash = Some(h.clone());