New Chain struct acting as facade for all chain operations

Introduce a new Chain struct that maintains the current head,
acts as facade to the store and the block processing pipeline
and handles all appropriate locking.

All higher level components, like the overall server, REST APIs
and miner have been update to only depend on Chain. Cleaned up
some duplication and tests now that there's a single entry point
to all blockchain operations.
This commit is contained in:
Ignotus Peverell
2017-07-03 16:46:25 -07:00
parent 8974cbc12c
commit 15ea8da34a
16 changed files with 432 additions and 414 deletions
+11 -36
View File
@@ -38,7 +38,7 @@ use util;
#[derive(Clone)]
pub struct ChainApi {
/// data store access
chain_store: Arc<chain::ChainStore>,
chain: Arc<chain::Chain>,
}
impl ApiEndpoint for ChainApi {
@@ -52,7 +52,7 @@ impl ApiEndpoint for ChainApi {
}
fn get(&self, id: String) -> ApiResult<Tip> {
self.chain_store.head().map_err(|e| Error::Internal(e.to_string()))
self.chain.head().map_err(|e| Error::Internal(format!("{:?}", e)))
}
}
@@ -60,8 +60,7 @@ impl ApiEndpoint for ChainApi {
#[derive(Clone)]
pub struct OutputApi {
/// data store access
chain_store: Arc<chain::ChainStore>,
chain_head: Arc<Mutex<chain::Tip>>,
chain: Arc<chain::Chain>,
}
impl ApiEndpoint for OutputApi {
@@ -77,34 +76,12 @@ impl ApiEndpoint for OutputApi {
fn get(&self, id: String) -> ApiResult<Output> {
debug!("GET output {}", id);
let c = util::from_hex(id.clone()).map_err(|e| Error::Argument(format!("Not a valid commitment: {}", id)))?;
let commitment = Commitment::from_vec(c);
// TODO use an actual UTXO tree
// in the meantime doing it the *very* expensive way:
// 1. check the output exists
// 2. run the chain back from the head to check it hasn't been spent
if let Ok(out) = self.chain_store.get_output_by_commit(&commitment) {
let mut block_h: Hash;
{
let chain_head = self.chain_head.clone();
let head = chain_head.lock().unwrap();
block_h = head.last_block_h;
}
loop {
let b = self.chain_store.get_block(&block_h)?;
for input in b.inputs {
if input.commitment() == commitment {
return Err(Error::NotFound);
}
}
if b.header.height == 1 {
return Ok(out);
} else {
block_h = b.header.previous;
}
}
}
Err(Error::NotFound)
match self.chain.get_unspent(&Commitment::from_vec(c)) {
Some(utxo) => Ok(utxo),
None => Err(Error::NotFound),
}
}
}
@@ -176,8 +153,7 @@ struct TxWrapper {
/// Start all server REST APIs. Just register all of them on a ApiServer
/// instance and runs the corresponding HTTP server.
pub fn start_rest_apis<T>(addr: String,
chain_store: Arc<chain::ChainStore>,
chain_head: Arc<Mutex<chain::Tip>>,
chain: Arc<chain::Chain>,
tx_pool: Arc<RwLock<pool::TransactionPool<T>>>)
where T: pool::BlockChain + Clone + Send + Sync + 'static
{
@@ -185,11 +161,10 @@ pub fn start_rest_apis<T>(addr: String,
thread::spawn(move || {
let mut apis = ApiServer::new("/v1".to_string());
apis.register_endpoint("/chain".to_string(),
ChainApi { chain_store: chain_store.clone() });
ChainApi { chain: chain.clone() });
apis.register_endpoint("/chain/utxo".to_string(),
OutputApi {
chain_store: chain_store.clone(),
chain_head: chain_head.clone(),
chain: chain.clone(),
});
apis.register_endpoint("/pool".to_string(), PoolApi { tx_pool: tx_pool });