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
}