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
125
126
127
128
129
130
use std::collections::HashMap;
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, Clone)]
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<(BitcoindTransaction, Vec<TransactionType>)> {
let mut all_transactions = vec![];
let mut transaction_hash: HashMap<String, Vec<TransactionType>> = HashMap::new();
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 {
let mut grouped_transactions = vec![];
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 {
grouped_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 {
grouped_transactions.push(TransactionType::Sent(
vout_for_vin.n,
transaction_for_vin.clone(),
tx.clone(),
));
}
}
None => {}
}
}
}
}
all_transactions.push((tx.clone(), grouped_transactions));
}
all_transactions
}