1
0
Fork 0
mirror of https://github.com/romanz/electrs.git synced 2025-02-24 15:02:21 +01:00

Handle block and script_hash subscription

This commit is contained in:
Roman Zeyde 2018-05-07 10:54:31 +03:00
parent 10c6daebbe
commit 92bfe88b46
No known key found for this signature in database
GPG key ID: 87CAE5FA46917CBB

View file

@ -39,10 +39,10 @@ fn history_from_status(status: &Status) -> Vec<(u32, Sha256dHash)> {
txns txns
} }
fn hash_from_status(status: &Status) -> Option<FullHash> { fn hash_from_status(status: &Status) -> Value {
let txns = history_from_status(status); let txns = history_from_status(status);
if txns.is_empty() { if txns.is_empty() {
return None; return Value::Null;
} }
let mut hash = FullHash::default(); let mut hash = FullHash::default();
@ -52,10 +52,10 @@ fn hash_from_status(status: &Status) -> Option<FullHash> {
sha2.input(part.as_bytes()); sha2.input(part.as_bytes());
} }
sha2.result(&mut hash); sha2.result(&mut hash);
Some(hash) Value::String(util::hexlify(&hash))
} }
fn format_header(header: &BlockHeader, height: usize) -> Value { fn jsonify_header(header: &BlockHeader, height: usize) -> Value {
json!({ json!({
"block_height": height, "block_height": height,
"version": header.version, "version": header.version,
@ -69,14 +69,25 @@ fn format_header(header: &BlockHeader, height: usize) -> Value {
struct Handler<'a> { struct Handler<'a> {
query: &'a Query<'a>, query: &'a Query<'a>,
headers_subscribe: bool,
status_hashes: HashMap<Sha256dHash, Value>, // ScriptHash -> StatusHash
} }
impl<'a> Handler<'a> { impl<'a> Handler<'a> {
fn blockchain_headers_subscribe(&self) -> Result<Value> { pub fn new(query: &'a Query) -> Handler<'a> {
Handler {
query: query,
headers_subscribe: false,
status_hashes: HashMap::new(),
}
}
fn blockchain_headers_subscribe(&mut self) -> Result<Value> {
let entry = self.query let entry = self.query
.get_best_header() .get_best_header()
.chain_err(|| "no headers found")?; .chain_err(|| "no headers found")?;
Ok(format_header(entry.header(), entry.height())) self.headers_subscribe = true;
Ok(jsonify_header(entry.header(), entry.height()))
} }
fn server_version(&self) -> Result<Value> { fn server_version(&self) -> Result<Value> {
@ -118,7 +129,7 @@ impl<'a> Handler<'a> {
let height = params.get(0).chain_err(|| "missing height")?; let height = params.get(0).chain_err(|| "missing height")?;
let height = height.as_u64().chain_err(|| "non-number height")? as usize; let height = height.as_u64().chain_err(|| "non-number height")? as usize;
let headers = self.query.get_headers(&vec![height]); let headers = self.query.get_headers(&vec![height]);
Ok(json!(format_header(&headers[0], height))) Ok(json!(jsonify_header(&headers[0], height)))
} }
fn blockchain_estimatefee(&self, _params: &[Value]) -> Result<Value> { fn blockchain_estimatefee(&self, _params: &[Value]) -> Result<Value> {
@ -129,14 +140,12 @@ impl<'a> Handler<'a> {
Ok(json!(1e-5)) // TODO: consult with actual node Ok(json!(1e-5)) // TODO: consult with actual node
} }
fn blockchain_scripthash_subscribe(&self, params: &[Value]) -> Result<Value> { 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 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 = hash_from_status(&status);
Ok(match hash_from_status(&status) { self.status_hashes.insert(script_hash, result.clone());
Some(hash) => Value::String(util::hexlify(&hash)), Ok(result)
None => Value::Null,
})
} }
fn blockchain_scripthash_get_balance(&self, params: &[Value]) -> Result<Value> { fn blockchain_scripthash_get_balance(&self, params: &[Value]) -> Result<Value> {
@ -180,7 +189,7 @@ impl<'a> Handler<'a> {
"pos": pos})) "pos": pos}))
} }
fn handle_command(&self, method: &str, params: &[Value], id: &Number) -> Result<Value> { fn handle_command(&mut self, method: &str, params: &[Value], id: &Number) -> Result<Value> {
let result = match method { let result = match method {
"blockchain.headers.subscribe" => self.blockchain_headers_subscribe(), "blockchain.headers.subscribe" => self.blockchain_headers_subscribe(),
"server.version" => self.server_version(), "server.version" => self.server_version(),
@ -203,7 +212,35 @@ impl<'a> Handler<'a> {
Ok(reply) Ok(reply)
} }
pub fn run(self, mut stream: TcpStream, addr: SocketAddr, chan: &Channel) -> Result<()> { fn update_subscriptions(&mut self, blockhash: &Sha256dHash) -> Result<Vec<Value>> {
info!("block {} found", blockhash);
let mut result = vec![];
if self.headers_subscribe {
let entry = self.query
.get_best_header()
.chain_err(|| "no headers found")?;
let header = jsonify_header(entry.header(), entry.height());
result.push(json!({
"jsonrpc": "2.0",
"method": "blockchain.headers.subscribe",
"params": [header]}));
}
for (script_hash, status_hash) in self.status_hashes.iter_mut() {
let status = self.query.status(&script_hash[..]);
let new_status_hash = hash_from_status(&status);
if new_status_hash == *status_hash {
continue;
}
result.push(json!({
"jsonrpc": "2.0",
"method": "blockchain.scripthash.subscribe",
"params": [script_hash.be_hex_string(), new_status_hash]}));
*status_hash = new_status_hash;
}
Ok(result)
}
pub fn run(mut self, mut stream: TcpStream, addr: SocketAddr, chan: &Channel) -> Result<()> {
let mut reader = BufReader::new(stream let mut reader = BufReader::new(stream
.try_clone() .try_clone()
.chain_err(|| "failed to clone TcpStream")?); .chain_err(|| "failed to clone TcpStream")?);
@ -246,7 +283,17 @@ impl<'a> Handler<'a> {
.write_all(line.as_bytes()) .write_all(line.as_bytes())
.chain_err(|| "failed to send response")?; .chain_err(|| "failed to send response")?;
} }
Message::Block(blockhash) => info!("block {} found", blockhash), Message::Block(blockhash) => {
for update in self.update_subscriptions(&blockhash)
.chain_err(|| "failed to get updates")?
{
debug!("update: {}", update);
let line = update.to_string() + "\n";
stream
.write_all(line.as_bytes())
.chain_err(|| "failed to send update")?;
}
}
Message::Done => break, Message::Done => break,
} }
} }
@ -287,7 +334,7 @@ pub fn serve(addr: &str, query: &Query, mut chan: Channel) {
loop { loop {
let (stream, addr) = listener.accept().unwrap(); let (stream, addr) = listener.accept().unwrap();
info!("[{}] connected peer", addr); info!("[{}] connected peer", addr);
let handler = Handler { query }; let handler = Handler::new(query);
match handler.run(stream, addr, &mut chan) { match handler.run(stream, addr, &mut chan) {
Ok(()) => info!("[{}] disconnected peer", addr), Ok(()) => info!("[{}] disconnected peer", addr),
Err(ref e) => { Err(ref e) => {