Skip to content
This repository was archived by the owner on Nov 6, 2020. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions ethcore/src/blockchain/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ pub struct BlockChain {
block_hashes: RwLock<HashMap<BlockNumber, H256>>,
transaction_addresses: RwLock<HashMap<H256, TransactionAddress>>,
block_receipts: RwLock<HashMap<H256, BlockReceipts>>,
bad_blocks: RwLock<HashMap<H256, BlockInfo>>,

db: Arc<BlockChainDB>,

Expand Down Expand Up @@ -527,6 +528,7 @@ impl BlockChain {
block_hashes: RwLock::new(HashMap::new()),
transaction_addresses: RwLock::new(HashMap::new()),
block_receipts: RwLock::new(HashMap::new()),
bad_blocks: RwLock::new(HashMap::new()),
db: db.clone(),
cache_man: Mutex::new(cache_man),
pending_best_block: RwLock::new(None),
Expand Down Expand Up @@ -985,6 +987,11 @@ impl BlockChain {

let info = self.block_info(&header, route, &extras);

if self.is_blacklisted_hash(hash) {
self.bad_blocks.write().insert(hash, info);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you describe how this lock is used? Locks are generally bad and increase complexity, but if its use and pattern is straightforward then it can obviously be completely fine.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason behind using a lock for those hashes is due to the potential for a blockchain client to try and read what the current bad blocks are. At the same time a bad block could be inserted into our hashmap of bad blocks. Thus a race condition for the same resource so it made sense to have a lock.

return ImportRoute::none();
}

if let BlockLocation::BranchBecomingCanonChain(ref d) = info.location {
info!(target: "reorg", "Reorg to {} ({} {} {})",
Colour::Yellow.bold().paint(format!("#{} {}", info.number, info.hash)),
Expand Down Expand Up @@ -1364,6 +1371,26 @@ impl BlockChain {
}
}

/// Determine if a hash is blacklisted (part of a hard fork)
fn is_blacklisted_hash(&self, hash: H256) -> bool {
if self.blacklisted_hashes().contains_key(&hash) {
return true
}
false
}

/// Get all blacklisted hashes
fn blacklisted_hashes(&self) -> HashMap<H256, bool> {
let mut blacklist = HashMap::new();
blacklist.insert("05bef30ef572270f654746da22639a7a0c97dd97a7050b9e252391996aaeb689".into(), true);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are these and why are they inserted here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These represent the list of bad hashes (usually hard forks) that determine whether a block is considered "bad". Similar to the implementation in geth: https://github.com/ethereum/go-ethereum/blob/master/core/blocks.go

blacklist.insert("7d05d08cbc596a2e5e4f13b80a743e53e09221b5323c3a61946b20873e58583f".into(), true);
blacklist
}

pub fn bad_blocks(&self) -> Vec<H256> {
self.bad_blocks.read().iter().map(|(hash, _)| *hash).collect::<Vec<H256>>()
}

/// Get best block hash.
pub fn best_block_hash(&self) -> H256 {
self.best_block.read().header.hash()
Expand Down
10 changes: 10 additions & 0 deletions ethcore/src/client/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1650,6 +1650,16 @@ impl BlockChainClient for Client {
Self::block_hash(&chain, id)
}

fn bad_blocks(&self) -> Option<Vec<H256>> {
let blocks = self.chain.read().bad_blocks();

if blocks.len() > 0 {
Some(blocks)
} else {
None
}
}

fn code(&self, address: &Address, state: StateOrBlock) -> Option<Option<Bytes>> {
let result = match state {
StateOrBlock::State(s) => s.code(address).ok(),
Expand Down
10 changes: 10 additions & 0 deletions ethcore/src/client/test_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,12 @@ impl TestBlockChainClient {
}
}

fn bad_blocks(&self) -> Option<Vec<H256>> {
let mut bad_block = Vec::new();
bad_block.push("05bef30ef572270f654746da22639a7a0c97dd97a7050b9e252391996aaeb689".into());
Some(bad_block)
}

/// Inserts a transaction with given gas price to miners transactions queue.
pub fn insert_transaction_with_gas_price_to_queue(&self, gas_price: U256) -> H256 {
let keypair = Random.generate().unwrap();
Expand Down Expand Up @@ -622,6 +628,10 @@ impl BlockChainClient for TestBlockChainClient {
Self::block_hash(self, id)
}

fn bad_blocks(&self) -> Option<Vec<H256>> {
Self::bad_blocks(self)
}

fn storage_root(&self, _address: &Address, _id: BlockId) -> Option<H256> {
None
}
Expand Down
3 changes: 3 additions & 0 deletions ethcore/src/client/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,9 @@ pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContra
/// Get block hash.
fn block_hash(&self, id: BlockId) -> Option<H256>;

/// Get all known bad blocks in chain.
fn bad_blocks(&self) -> Option<Vec<H256>>;

/// Get address code at given block's state.
fn code(&self, address: &Address, state: StateOrBlock) -> Option<Option<Bytes>>;

Expand Down
16 changes: 16 additions & 0 deletions rpc/src/v1/impls/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,22 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> Eth for EthClient<
Ok(true)
}

fn bad_blocks(&self, include_txs: bool) -> BoxFuture<Vec<RichBlock>> {
let blocks = self.client.bad_blocks()
.and_then(|vec| {
vec.iter()
.filter_map(|hash| {
self.rich_block(BlockNumberOrId::Id(BlockId::Hash(*hash)), include_txs).ok()
})
.collect()
});

match blocks {
Some(b) => Box::new(future::done(Ok(b))),
None => Box::new(future::done(Ok(<Vec<RichBlock>>::new())))
}
}

fn send_raw_transaction(&self, raw: Bytes) -> Result<RpcH256> {
Rlp::new(&raw.into_vec()).as_val()
.map_err(errors::rlp)
Expand Down
4 changes: 4 additions & 0 deletions rpc/src/v1/impls/light/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,10 @@ impl<T: LightChainClient + 'static> Eth for EthClient<T> {
fn submit_hashrate(&self, _rate: RpcU256, _id: RpcH256) -> Result<bool> {
Err(errors::light_unimplemented(None))
}

fn bad_blocks(&self, _include_txs: bool) -> BoxFuture<Vec<RichBlock>> {
Box::new(future::done(Ok(<Vec<RichBlock>>::new())))
}
}

// This trait implementation triggers a blanked impl of `EthFilter`.
Expand Down
4 changes: 4 additions & 0 deletions rpc/src/v1/traits/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,10 @@ build_rpc_trait! {
/// Used for submitting mining hashrate.
#[rpc(name = "eth_submitHashrate")]
fn submit_hashrate(&self, U256, H256) -> Result<bool>;

/// Used for finding bad blocks in current chain.
#[rpc(name = "debug_getBadBlocks")]
fn bad_blocks(&self, bool) -> BoxFuture<Vec<RichBlock>>;
}
}

Expand Down