1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use bitcoind_request::{
    client::Client as BitcoindRequestClient,
    command::{
        get_raw_transaction::{
            GetRawTransactionCommand, GetRawTransactionCommandResponse,
            Transaction as BitcoindTransaction, Vin,
        },
        CallableCommand,
    },
};
use electrs_query::{
    self, get_balance_for_address, get_historical_transactions_for_address, get_utxos_for_address,
    Client as ElectrsClient,
};

pub type VinIndex = u64;
pub type VoutIndex = u64;

pub type SpentFromTransaction = BitcoindTransaction;
pub type SpentInTransaction = BitcoindTransaction;

#[derive(Debug)]
pub enum TransactionType {
    Sent(VinIndex, SpentFromTransaction, SpentInTransaction),
    Recieved(VoutIndex, BitcoindTransaction),
}
fn get_transaction(
    txid: String,
    bitcoind_request_client: &BitcoindRequestClient,
) -> BitcoindTransaction {
    let transaction_response = GetRawTransactionCommand::new(txid.to_string())
        .verbose(true)
        .call(&bitcoind_request_client)
        .unwrap();
    let transaction_result = match transaction_response {
        GetRawTransactionCommandResponse::Transaction(transaction) => Ok(transaction),
        _ => Err("shouldn't reach"),
    };
    transaction_result.unwrap()
}

fn get_raw_transactions_for_address(
    address: &str,
    electrs_client: &ElectrsClient,
    bitcoind_request_client: &BitcoindRequestClient,
) -> Vec<BitcoindTransaction> {
    let history = get_historical_transactions_for_address(&address, &electrs_client);
    let transactions: Vec<BitcoindTransaction> = history
        .iter()
        .map(|historical_transaction| {
            let txid = &historical_transaction.tx_hash;
            let transaction_response = GetRawTransactionCommand::new(txid.to_string())
                .verbose(true)
                .call(&bitcoind_request_client)
                .unwrap();
            let transaction_result = match transaction_response {
                GetRawTransactionCommandResponse::Transaction(transaction) => Ok(transaction),
                _ => Err("shouldn't reach"),
            };
            transaction_result.unwrap()
        })
        .collect();
    transactions
}

pub fn get_all_transactions_for_address(
    address: &str,
    electrs_client: &ElectrsClient,
    bitcoind_request_client: &BitcoindRequestClient,
) -> Vec<TransactionType> {
    let mut all_transactions = vec![];
    let balance = get_balance_for_address(&address, &electrs_client);
    let utxos = get_utxos_for_address(&address, &electrs_client);
    let history = get_historical_transactions_for_address(&address, &electrs_client);
    let transactions =
        get_raw_transactions_for_address(&address, &electrs_client, &bitcoind_request_client);
    for tx in &transactions {
        for vout in tx.vout.clone() {
            let vout_address = if vout.script_pub_key.address.is_some() {
                vout.script_pub_key.address
            } else {
                vout.address
            };
            match vout_address {
                Some(addr) => {
                    if addr == address {
                        all_transactions.push(TransactionType::Recieved(vout.n, tx.clone()));
                    }
                }
                None => {}
            }
        }
        for vin in tx.vin.clone() {
            match vin {
                Vin::Coinbase(vin) => {
                    todo!()
                }
                Vin::NonCoinbase(vin) => {
                    let transaction_for_vin = get_transaction(vin.txid, &bitcoind_request_client);
                    let vout_for_vin = &transaction_for_vin.vout[vin.vout as usize];
                    let vout_address = if vout_for_vin.script_pub_key.address.is_some() {
                        &vout_for_vin.script_pub_key.address
                    } else {
                        &vout_for_vin.address
                    }
                    .clone();
                    match vout_address {
                        Some(addr) => {
                            if addr == address {
                                all_transactions.push(TransactionType::Sent(
                                    vout_for_vin.n,
                                    transaction_for_vin.clone(),
                                    tx.clone(),
                                ));
                            }
                        }
                        None => {}
                    }
                }
            }
        }
    }
    all_transactions
}