1
0
mirror of https://github.com/romanz/electrs.git synced 2024-11-19 01:43:29 +01:00
This commit is contained in:
Roman Zeyde 2018-04-25 22:44:36 +03:00
parent 2657db6a21
commit 4ba7e0092d
No known key found for this signature in database
GPG Key ID: 87CAE5FA46917CBB
5 changed files with 188 additions and 1 deletions

View File

@ -9,6 +9,7 @@ arrayref = "0.3.4"
bincode = "1.0.0"
bitcoin = "0.12"
crossbeam = "0.3.2"
error-chain = "0.11"
itertools = "0.7.8"
log = "0.4"
pbr = "1.0.0"

View File

@ -7,7 +7,7 @@ extern crate zmq;
use argparse::{ArgumentParser, StoreFalse};
use std::fs::OpenOptions;
use indexrs::{daemon, index, store, waiter};
use indexrs::{daemon, index, rpc, store, waiter};
fn setup_logging() {
use simplelog::*;
@ -68,6 +68,7 @@ fn run_server(config: Config) {
{
crossbeam::scope(|scope| {
scope.spawn(|| handle_queries(&store, &daemon));
scope.spawn(|| rpc::serve());
if config.enable_indexing {
loop {
if store.read_header(&waiter.wait()).is_none() {

View File

@ -1,3 +1,5 @@
#![recursion_limit = "1024"]
extern crate bincode;
extern crate bitcoin;
extern crate crypto;
@ -12,12 +14,17 @@ extern crate zmq;
#[macro_use]
extern crate arrayref;
#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
pub mod daemon;
pub mod index;
pub mod rpc;
pub mod store;
pub mod waiter;

131
src/rpc.rs Normal file
View File

@ -0,0 +1,131 @@
use serde_json::{from_str, Number, Value};
use std::net::{SocketAddr, TcpListener, TcpStream};
use std::io::{BufRead, BufReader, Write};
error_chain!{}
fn blockchain_headers_subscribe() -> Result<Value> {
Ok(json!({}))
}
fn server_version() -> Result<Value> {
Ok(json!(["LES 0.1.0", "1.2"]))
}
fn server_banner() -> Result<Value> {
Ok(json!("Welcome to Local Electrum Server!\n"))
}
fn server_donation_address() -> Result<Value> {
Ok(json!("No, thanks :)\n"))
}
fn server_peers_subscribe() -> Result<Value> {
Ok(json!([]))
}
fn mempool_get_fee_histogram() -> Result<Value> {
Ok(json!([])) // TODO: consult with actual mempool
}
fn blockchain_estimatefee(_params: &[&str]) -> Result<Value> {
Ok(json!(1e-5)) // TODO: consult with actual mempool
}
fn blockchain_scripthash_subscribe(_params: &[&str]) -> Result<Value> {
Ok(json!("HEX_STATUS"))
}
fn blockchain_scripthash_get_history(_params: &[&str]) -> Result<Value> {
Ok(json!([])) // TODO: list of {tx_hash: "ABC", height: 123}
}
fn blockchain_transaction_get(_params: &[&str]) -> Result<Value> {
Ok(json!("HEX_TX")) // TODO: list of {tx_hash: "ABC", height: 123}
}
fn blockchain_transaction_get_merkle(_params: &[&str]) -> Result<Value> {
Ok(json!({"block_height": 123, "merkle": ["A", "B", "C"], "pos": 45}))
}
fn handle_command(method: &str, params_values: &[Value], id: &Number) -> Result<Value> {
let mut params = Vec::<&str>::new();
for value in params_values {
if let Some(s) = value.as_str() {
params.push(s);
} else {
bail!("invalid param: {:?}", value);
}
}
let result = match method {
"blockchain.headers.subscribe" => blockchain_headers_subscribe(),
"server.version" => server_version(),
"server.banner" => server_banner(),
"server.donation_address" => server_donation_address(),
"server.peers.subscribe" => server_peers_subscribe(),
"mempool.get_fee_histogram" => mempool_get_fee_histogram(),
"blockchain.estimatefee" => blockchain_estimatefee(&params),
"blockchain.scripthash.subscribe" => blockchain_scripthash_subscribe(&params),
"blockchain.scripthash.get_history" => blockchain_scripthash_get_history(&params),
"blockchain.transaction.get" => blockchain_transaction_get(&params),
"blockchain.transaction.get_merkle" => blockchain_transaction_get_merkle(&params),
&_ => bail!("unknown method {} {:?}", method, params),
}?;
let reply = json!({"jsonrpc": "2.0", "id": id, "result": result});
Ok(reply)
}
fn handle_client(mut stream: TcpStream, addr: SocketAddr) -> Result<()> {
let mut reader = BufReader::new(stream
.try_clone()
.chain_err(|| "failed to clone TcpStream")?);
let mut line = String::new();
loop {
line.clear();
reader
.read_line(&mut line)
.chain_err(|| "failed to read a request")?;
if line.is_empty() {
break;
}
let line = line.trim_right();
let cmd: Value = from_str(line).chain_err(|| "invalid JSON format")?;
let reply = match (cmd.get("method"), cmd.get("params"), cmd.get("id")) {
(
Some(&Value::String(ref method)),
Some(&Value::Array(ref params)),
Some(&Value::Number(ref id)),
) => handle_command(method, params, id)?,
_ => bail!("invalid command: {}", cmd),
};
info!("[{}] {} -> {}", addr, cmd, reply);
let mut line = reply.to_string();
line.push_str("\n");
stream
.write_all(line.as_bytes())
.chain_err(|| "failed to send response")?;
}
Ok(())
}
pub fn serve() {
let listener = TcpListener::bind("127.0.0.1:50001").unwrap();
loop {
let (stream, addr) = listener.accept().unwrap();
info!("[{}] connected peer", addr);
match handle_client(stream, addr) {
Ok(()) => info!("[{}] disconnected peer", addr),
Err(ref e) => {
error!("[{}] {}", addr, e);
for e in e.iter().skip(1) {
error!("caused by: {}", e);
}
}
}
}
}

47
tools/client.py Normal file
View File

@ -0,0 +1,47 @@
import hashlib
import sys
from logbook import Logger, StreamHandler
from pycoin.coins.bitcoin.networks import BitcoinMainnet
import pycoin.ui.key_from_text
import pycoin.key
import zmq
script_for_address = BitcoinMainnet.ui.script_for_address
log = Logger(__name__)
def main():
c = zmq.Context()
s = c.socket(zmq.REQ)
s.connect('ipc:///tmp/indexrs.rpc')
xpub, = sys.argv[1:]
total = 0
k = pycoin.ui.key_from_text.key_from_text(xpub)
for change in (0, 1):
empty = 0
for n in range(100):
address = k.subkey(change).subkey(n).address()
script = script_for_address(address)
script_hash = hashlib.sha256(script).digest()
s.send(script_hash)
res = s.recv()
b = float(res)
total += b
if b:
log.info('{}/{} => {} has {:11.8f} BTC',
change, n, address, b)
empty = 0
else:
empty += 1
if empty >= 10:
break
log.info('total balance: {} BTC', total)
if __name__ == '__main__':
with StreamHandler(sys.stderr, level='INFO').applicationbound():
main()