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:
parent
10c6daebbe
commit
92bfe88b46
1 changed files with 64 additions and 17 deletions
81
src/rpc.rs
81
src/rpc.rs
|
@ -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) => {
|
||||||
|
|
Loading…
Add table
Reference in a new issue