Skip to content

Instantly share code, notes, and snippets.

@RCasatta
Last active March 12, 2020 22:14
Show Gist options
  • Save RCasatta/d8b071fcd0140f3728040acb280805fc to your computer and use it in GitHub Desktop.
Save RCasatta/d8b071fcd0140f3728040acb280805fc to your computer and use it in GitHub Desktop.
use electrum_client::{Client, GetHistoryRes};
use bitcoin::util::bip32::{ChildNumber, ExtendedPubKey};
use bitcoin::secp256k1::{Secp256k1, All};
use bitcoin::{Address, Network, Script, Transaction, BlockHeader};
use bitcoin::consensus::{serialize, deserialize};
use std::str::FromStr;
use std::time::Instant;
use std::collections::{HashSet, HashMap};
use sled;
const BATCH_SIZE:u32 = 20;
fn main(){
let db = sled::open("/tmp/db").unwrap();
let start = Instant::now();
let secp = Secp256k1::new();
let xpub = "tpubD6NzVbkrYhZ4YRJLsDvYBaXK6EFi9MSV34h8BAvHPzW8RtUpJpBFiL23hRnvrRUcb6Fz9eKiVG8EzZudGXYfdo5tiP8BuhrsBmFAsREPZG4";
let xpub = ExtendedPubKey::from_str(xpub).unwrap();
let mut client = Client::new("tn.not.fyi:55001").unwrap();
// load tx from db in map
let mut tx_height = HashMap::new();
let mut my_tx_ids = HashSet::new();
let mut heights = HashSet::new();
for i in 0..=1 {
let mut batch_count = 0;
loop {
let batch = get_script_batch(&secp, &xpub, i, batch_count);
let result: Vec<GetHistoryRes> = client.batch_script_get_history(&batch).unwrap().into_iter().flatten().collect();
println!("{}/batch({}) {:?}",i, batch_count, result.len());
if result.is_empty() {
break;
}
for el in result {
if el.height >= 0 {
heights.insert(el.height as usize);
}
my_tx_ids.insert(el.tx_hash);
tx_height.insert(el.tx_hash, el.height);
}
batch_count += 1;
}
}
println!("elapsed {}", (Instant::now()-start).as_millis() );
let mut txs_to_download = Vec::new();
for tx_id in my_tx_ids.iter() {
if db.get(tx_id).unwrap().is_none() {
txs_to_download.push(tx_id);
}
}
if !txs_to_download.is_empty() {
let txs_downloaded = client.batch_transaction_get(txs_to_download).unwrap();
println!("txs_downloaded {:?} {}", txs_downloaded.len(), (Instant::now() - start).as_millis());
let mut previous_txs_set = HashSet::new();
for tx in txs_downloaded.iter() {
db.insert(tx.txid(), serialize(tx)).unwrap();
for input in tx.input.iter() {
previous_txs_set.insert(input.previous_output.txid);
}
}
let mut previous_txs_vec = vec![];
for tx_id in previous_txs_set {
if db.get(&tx_id).unwrap().is_none() {
previous_txs_vec.push(tx_id);
}
}
let txs_downloaded = client.batch_transaction_get(&previous_txs_vec).unwrap();
println!("previous txs_downloaded {:?} {}", txs_downloaded.len(), (Instant::now() - start).as_millis());
for tx in txs_downloaded.iter() {
db.insert(tx.txid(), serialize(tx)).unwrap();
}
}
let mut heights_to_download = Vec::new();
for height in heights {
if db.get(height.to_be_bytes()).unwrap().is_none() {
heights_to_download.push(height);
}
}
if !heights_to_download.is_empty() {
let headers_downloaded = client.batch_block_header(heights_to_download.clone()).unwrap();
for (header, height) in headers_downloaded.iter().zip(heights_to_download.iter()) {
db.insert(height.to_be_bytes(), serialize(header)).unwrap();
}
println!("headers_downloaded {:?} {}", headers_downloaded.len(), (Instant::now() - start).as_millis());
}
for tx_id in my_tx_ids.iter() {
let tx_bytes = db.get(tx_id).unwrap().unwrap();
let tx: Transaction = deserialize(&tx_bytes).unwrap();
let height = *tx_height.get(tx_id).unwrap() as usize; // FIXME
let header_bytes = db.get(height.to_be_bytes()).unwrap().unwrap();
let header: BlockHeader = deserialize(&header_bytes).unwrap();
let total_output: u64 = tx.output.iter().map(|o| o.value).sum();
let mut total_input = 0u64;
for input in tx.input.iter() {
let prevout = &input.previous_output.txid;
let prev_tx: Transaction = deserialize( &db.get(prevout).unwrap().unwrap() ).unwrap();
total_input += prev_tx.output[input.previous_output.vout as usize].value;
}
let fee = total_input - total_output;
println!("{} {} {} {:6}", tx_id, height, header.time, fee);
}
}
fn get_script_batch(secp: &Secp256k1<All>, xpub: &ExtendedPubKey, int_or_ext: u32, batch: u32) -> Vec<Script> {
let mut result = vec![];
let first_path = [ChildNumber::from(int_or_ext)];
let first_deriv = xpub.derive_pub(&secp, &first_path).unwrap();
let start = batch * BATCH_SIZE;
let end = start + BATCH_SIZE;
for i in start..end {
let second_path = [ChildNumber::from(i)];
let second_deriv = first_deriv.derive_pub(&secp, &second_path).unwrap();
//let address = Address::p2shwpkh(&second_deriv.public_key, Network::Testnet);
let address = Address::p2wpkh(&second_deriv.public_key, Network::Testnet);
//println!("{}/{} {}", int_or_ext, i, address);
result.push(address.script_pubkey());
}
result
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment