Last active
March 12, 2020 22:14
-
-
Save RCasatta/d8b071fcd0140f3728040acb280805fc to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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