1
0
mirror of https://github.com/romanz/electrs.git synced 2024-11-19 09:54:09 +01:00

Add proper error handling (instead of unwrapping) to query.rs

This commit is contained in:
Roman Zeyde 2018-05-25 19:08:20 +03:00
parent 0d56b2ecc6
commit 2355dbd0ed
No known key found for this signature in database
GPG Key ID: 87CAE5FA46917CBB
2 changed files with 41 additions and 37 deletions

View File

@ -138,30 +138,30 @@ impl Query {
}
}
fn load_txns(&self, store: &ReadStore, prefixes: Vec<HashPrefix>) -> Vec<TxnHeight> {
fn load_txns(&self, store: &ReadStore, prefixes: Vec<HashPrefix>) -> Result<Vec<TxnHeight>> {
let mut txns = Vec::new();
for txid_prefix in prefixes {
for tx_row in txrows_by_prefix(store, &txid_prefix) {
let txid: Sha256dHash = deserialize(&tx_row.key.txid).unwrap();
let txn: Transaction = self.get_tx(&txid);
let txn: Transaction = self.get_tx(&txid)?;
txns.push(TxnHeight {
txn,
height: tx_row.height,
})
}
}
txns
Ok(txns)
}
fn find_spending_input(
&self,
store: &ReadStore,
funding: &FundingOutput,
) -> Option<SpendingInput> {
) -> Result<Option<SpendingInput>> {
let spending_txns: Vec<TxnHeight> = self.load_txns(
store,
txids_by_funding_output(store, &funding.txn_id, funding.output_index),
);
)?;
let mut spending_inputs = Vec::new();
for t in &spending_txns {
for input in t.txn.input.iter() {
@ -177,11 +177,11 @@ impl Query {
}
}
assert!(spending_inputs.len() <= 1);
if spending_inputs.len() == 1 {
Ok(if spending_inputs.len() == 1 {
Some(spending_inputs.remove(0))
} else {
None
}
})
}
fn find_funding_outputs(&self, t: &TxnHeight, script_hash: &[u8]) -> Vec<FundingOutput> {
@ -200,57 +200,57 @@ impl Query {
result
}
fn confirmed_status(&self, script_hash: &[u8]) -> (Vec<FundingOutput>, Vec<SpendingInput>) {
fn confirmed_status(
&self,
script_hash: &[u8],
) -> Result<(Vec<FundingOutput>, Vec<SpendingInput>)> {
let mut funding = vec![];
let mut spending = vec![];
for t in self.load_txns(
self.app.store(),
txids_by_script_hash(self.app.store(), script_hash),
) {
)? {
funding.extend(self.find_funding_outputs(&t, script_hash));
}
for funding_output in &funding {
if let Some(spent) = self.find_spending_input(self.app.store(), &funding_output) {
if let Some(spent) = self.find_spending_input(self.app.store(), &funding_output)? {
spending.push(spent);
}
}
(funding, spending)
Ok((funding, spending))
}
fn mempool_status(
&self,
script_hash: &[u8],
confirmed_funding: &[FundingOutput],
) -> (Vec<FundingOutput>, Vec<SpendingInput>) {
) -> Result<(Vec<FundingOutput>, Vec<SpendingInput>)> {
let mut funding = vec![];
let mut spending = vec![];
let tracker = self.tracker.read().unwrap();
for t in self.load_txns(
tracker.index(),
txids_by_script_hash(tracker.index(), script_hash),
) {
)? {
funding.extend(self.find_funding_outputs(&t, script_hash));
}
// // TODO: dedup outputs (somehow) both confirmed and in mempool (e.g. reorg?)
for funding_output in funding.iter().chain(confirmed_funding.iter()) {
if let Some(spent) = self.find_spending_input(tracker.index(), &funding_output) {
if let Some(spent) = self.find_spending_input(tracker.index(), &funding_output)? {
spending.push(spent);
}
}
(funding, spending)
Ok((funding, spending))
}
pub fn status(&self, script_hash: &[u8]) -> Status {
let confirmed = self.confirmed_status(script_hash);
let mempool = self.mempool_status(script_hash, &confirmed.0);
Status { confirmed, mempool }
pub fn status(&self, script_hash: &[u8]) -> Result<Status> {
let confirmed = self.confirmed_status(script_hash)?;
let mempool = self.mempool_status(script_hash, &confirmed.0)?;
Ok(Status { confirmed, mempool })
}
pub fn get_tx(&self, tx_hash: &Sha256dHash) -> Transaction {
self.app
.daemon()
.gettransaction(tx_hash)
.expect(&format!("failed to load tx {}", tx_hash))
pub fn get_tx(&self, tx_hash: &Sha256dHash) -> Result<Transaction> {
self.app.daemon().gettransaction(tx_hash)
}
pub fn get_headers(&self, heights: &[usize]) -> Vec<BlockHeader> {
@ -276,12 +276,19 @@ impl Query {
&self,
tx_hash: &Sha256dHash,
height: usize,
) -> Option<(Vec<Sha256dHash>, usize)> {
) -> Result<(Vec<Sha256dHash>, usize)> {
let header_list = self.app.index().headers_list();
let blockhash = header_list.headers().get(height)?.hash();
let blockhash = header_list
.headers()
.get(height)
.chain_err(|| format!("missing block #{}", height))?
.hash();
let block: Block = self.app.daemon().getblock(&blockhash).unwrap();
let mut txids: Vec<Sha256dHash> = block.txdata.iter().map(|tx| tx.txid()).collect();
let pos = txids.iter().position(|txid| txid == tx_hash)?;
let pos = txids
.iter()
.position(|txid| txid == tx_hash)
.chain_err(|| format!("missing txid {}", tx_hash))?;
let mut merkle = Vec::new();
let mut index = pos;
while txids.len() > 1 {
@ -297,14 +304,11 @@ impl Query {
.map(|pair| merklize(pair[0], pair[1]))
.collect()
}
Some((merkle, pos))
Ok((merkle, pos))
}
pub fn broadcast(&self, txn: &Transaction) -> Result<Sha256dHash> {
self.app
.daemon()
.broadcast(txn)
.chain_err(|| "broadcast failed")
self.app.daemon().broadcast(txn)
}
pub fn update_mempool(&self) -> Result<()> {

View File

@ -118,7 +118,7 @@ impl Connection {
fn blockchain_scripthash_subscribe(&mut self, params: &[Value]) -> Result<Value> {
let script_hash = hash_from_value(params.get(0)).chain_err(|| "bad script_hash")?;
let status = self.query.status(&script_hash[..]);
let status = self.query.status(&script_hash[..])?;
let result = status.hash().map_or(Value::Null, |h| json!(hex::encode(h)));
self.status_hashes.insert(script_hash, result.clone());
Ok(result)
@ -126,7 +126,7 @@ impl Connection {
fn blockchain_scripthash_get_balance(&self, params: &[Value]) -> Result<Value> {
let script_hash = hash_from_value(params.get(0)).chain_err(|| "bad script_hash")?;
let status = self.query.status(&script_hash[..]);
let status = self.query.status(&script_hash[..])?;
Ok(
json!({ "confirmed": status.confirmed_balance(), "unconfirmed": status.mempool_balance() }),
)
@ -134,7 +134,7 @@ impl Connection {
fn blockchain_scripthash_get_history(&self, params: &[Value]) -> Result<Value> {
let script_hash = hash_from_value(params.get(0)).chain_err(|| "bad script_hash")?;
let status = self.query.status(&script_hash[..]);
let status = self.query.status(&script_hash[..])?;
Ok(json!(Value::Array(
status
.history()
@ -156,7 +156,7 @@ impl Connection {
fn blockchain_transaction_get(&self, params: &[Value]) -> Result<Value> {
// TODO: handle 'verbose' param
let tx_hash = hash_from_value(params.get(0)).chain_err(|| "bad tx_hash")?;
let tx = self.query.get_tx(&tx_hash);
let tx = self.query.get_tx(&tx_hash)?;
Ok(json!(hex::encode(&serialize(&tx).unwrap())))
}
@ -217,7 +217,7 @@ impl Connection {
}
}
for (script_hash, status_hash) in self.status_hashes.iter_mut() {
let status = self.query.status(&script_hash[..]);
let status = self.query.status(&script_hash[..])?;
let new_status_hash = status.hash().map_or(Value::Null, |h| json!(hex::encode(h)));
if new_status_hash == *status_hash {
continue;