blockchain_cycle_finder/lib.rs
1mod cycle;
2pub mod db_functions;
3
4
5pub mod blockchain {
6
7 use std::{fs::File, io::{BufWriter, BufReader}, str::FromStr};
8 use bitcoin_explorer::{BitcoinDB,SBlock, Address, STransaction,Txid};
9 use array_tool::vec::*;
10 use crate::{cycle::Cycle, db_functions::*};
11 use serde_json::to_writer;
12 //use std::time::{Duration, Instant};
13
14 /// This function iterates in 'db_b', the blocks from 'm' to 'n' range to extract the contained transactions to a JSON file in 'path' and returns the number of transactions extracted.
15 ///
16 /// # Arguments
17 ///
18 /// * `db_b` - A BitcoinDB instance from the library 'bitcoin_explorer'.
19 /// * `m` - Number that specifies bottom boundle of the range of blocks to iterate.
20 /// * `n` - Number that specifies upper boundle of the range of blocks to iterate.
21 /// * `path` - String that defines the path of the JSON file where the transactions will been extracted.
22 ///
23 /// # Examples
24 /// ```
25 /// use std::path::Path;
26 /// use ext::blockchain::*;
27 /// use bitcoin_explorer::BitcoinDB;
28 ///
29 /// let db_b = BitcoinDB::new(Path::new("/home/ubuntu/.bitcoin/"), true).unwrap();
30 /// let num = extract(&db_b, &0, &1000, "./transactions.json");
31 /// ```
32 ///
33 ///
34 pub fn extract(db_b: &BitcoinDB,m:&usize, n:&usize, path:&str) -> usize{
35
36 let mut keys = Vec::new();
37 let mut values= Vec::new();
38
39 for block in db_b.iter_block::<SBlock>(*m, *n) {
40 for tx in block.txdata.iter() {
41 let txid = bincode::serialize(&tx.txid).unwrap();
42 let tx_encoded= bincode::serialize(&tx).unwrap();
43 keys.push(txid);
44 values.push(tx_encoded);
45
46 }
47 }
48
49 let transactions = vec![keys,values];
50
51 let output = File::create(path).unwrap();
52 let writer = BufWriter::new(output);
53 let _ = to_writer(writer,&transactions).unwrap();
54
55 transactions.get(0).unwrap().len()
56 }
57
58 /// The function reads the file located in the given 'path' and inserts the transactions readed in the database 'db'.
59 ///
60 /// # Arguments
61 ///
62 /// * `db` - A 'Db' struct that manage a RocksDB instance.
63 /// * `path` - String that defines the path of the JSON file where the transactions has been extracted.
64 ///
65 /// # Examples
66 /// ```
67 /// use ext::blockchain::*;
68 /// use ext::db_functions::Db;
69 ///
70 /// let db = Db::new("/tmp/.rocksdb");
71 /// load(&db, "./transactions.json");
72 /// ```
73 ///
74 ///
75 pub fn load(db:&Db, path:&str){
76 let file = File::open(path).unwrap();
77 let reader = BufReader::new(file);
78 let transactions: Vec<Vec<Vec<u8>>> = serde_json::from_reader(reader).unwrap();
79
80 let txid = transactions.get(0).unwrap();
81 let tx = transactions.get(1).unwrap();
82
83 for i in 0..txid.len(){
84 db.put_tx(&i, txid.get(i).unwrap().clone(), tx.get(i).unwrap().clone());
85 }
86
87 }
88
89 /// Given a 'Db' struct that manage the RocksDB instance with the transactions loaded, iterates the registers until 'n' register to search possibles cycles and return a vector with them.
90 ///
91 /// # Arguments
92 ///
93 /// * `db` - A 'Db' struct that manage a RocksDB instance.
94 /// * `n` - Number that specifies upper boundle of the range of blocks to iterate.
95 ///
96 /// # Examples
97 /// ```
98 /// use std::path::Path;
99 /// use ext::blockchain::*;
100 /// use ext::db_functions::Db;
101 /// use bitcoin_explorer::BitcoinDB;
102 ///
103 /// let db_b = BitcoinDB::new(Path::new("/home/ubuntu/.bitcoin/"), true).unwrap();
104 /// let num = extract(&db_b, &0, &1000, "./transactions.json");
105 /// let db = Db::new("/tmp/.rocksdb");
106 /// load(&db, "./transactions.json");
107 /// let possible_cycles = search(&db,&num);
108 /// ```
109 ///
110 ///
111 pub fn search(db: &Db,n:&usize) -> Vec<Cycle>{
112 let mut iter = db.get_iterator(&0,n);
113 let mut addresses:Vec<Address> = Vec::new();
114 let mut possibles_cycles:Vec<Cycle> = Vec::new();
115
116 iter.seek_to_first();
117 while iter.valid() {
118 let tx = bincode::deserialize(iter.value().unwrap()).unwrap();
119 let mut in_address:Vec<Address> = Vec::new();
120
121 if !is_coinbase(&tx){
122
123 for input in tx.input.unique().iter() {
124 let clave = bincode::serialize(&input.txid).unwrap();
125 let res = db.get_tx(&clave);
126
127
128 if !res.is_none(){
129 let utxo_tx:STransaction = bincode::deserialize(&res.unwrap()).unwrap();
130
131 let output = &utxo_tx.output[input.vout as usize];
132 if !output.addresses.is_empty(){
133 let out_addr = &output.addresses[0];
134
135 in_address.push(out_addr.clone());
136
137 if !in_address.contains(out_addr){
138 in_address.push(out_addr.clone());
139 }
140
141 if !addresses.contains(out_addr){
142 addresses.push(out_addr.clone());
143 }
144
145 }
146 }
147 }
148
149 for output in tx.output.iter() {
150 if !output.addresses.is_empty(){
151 let out_addr = &output.addresses[0];
152 if !in_address.contains(out_addr){
153 if addresses.contains(out_addr){
154 possibles_cycles.push(Cycle::new(tx.clone(), out_addr.clone()));
155 }
156 }
157 }
158 }
159
160 }
161 iter.next();
162 }
163 possibles_cycles
164 }
165
166
167 /// Given a 'Db' struct that manage the RocksDB instance with the transactions loaded, a target address and a origin transaction, iterates the registers from 'm' to 'n' range to search possibles cycles for the target address and return a vector with them.
168 ///
169 /// # Arguments
170 ///
171 /// * `db` - A 'Db' struct that manage a RocksDB instance.
172 /// * `addr` - String that defines the target address in the Bitcoin Blockchain.
173 /// * `origen` - String that defines the origin transaction in the Bitcoin Blockchain.
174 /// * `m` - Number that specifies bottom boundle of the range of blocks to iterate.
175 /// * `n` - Number that specifies upper boundle of the range of blocks to iterate.
176 ///
177 /// # Examples
178 /// ```
179 /// use std::path::Path;
180 /// use ext::blockchain::*;
181 /// use ext::db_functions::Db;
182 /// use bitcoin_explorer::BitcoinDB;
183 ///
184 /// let db_b = BitcoinDB::new(Path::new("/home/ubuntu/.bitcoin/"), true).unwrap();
185 /// let num = extract(&db_b, &0, &2000, "./transactions.json");
186 /// let db = Db::new("/tmp/.rocksdb");
187 /// load(&db, "./transactions.json");
188 /// let possible_cycles = search_by_address(&db,"1DZTzaBHUDM7T3QvUKBz4qXMRpkg8jsfB5","35288d269cee1941eaebb2ea85e32b42cdb2b04284a56d8b14dcc3f5c65d6055",&999,&num);
189 /// ```
190 ///
191 ///
192 pub fn search_by_address(db: &Db,addr:&str,origen:&str,m:&usize,n:&usize) -> Vec<Cycle>{
193 let mut iter = db.get_iterator(m,n);
194 let mut possibles_cycles:Vec<Cycle> = Vec::new();
195 let address = Address::from_str(addr).unwrap();
196 let txid_origin = Txid::from_str(origen).unwrap();
197
198 iter.seek_to_first();
199 while iter.valid() {
200 let mut not_return_address = true;
201 let tx = bincode::deserialize(iter.value().unwrap()).unwrap();
202
203 if !is_coinbase(&tx) && txid_origin.ne(&tx.txid) {
204
205 for input in tx.input.unique().iter() {
206 let clave = bincode::serialize(&input.txid).unwrap();
207 let res = db.get_tx(&clave);
208
209
210 if !res.is_none(){
211 let utxo_tx:STransaction = bincode::deserialize(&res.unwrap()).unwrap();
212
213 let output = &utxo_tx.output[input.vout as usize];
214 if !output.addresses.is_empty(){
215 let out_addr = &output.addresses[0];
216 if address.eq(&out_addr){
217 not_return_address = false;
218 }
219
220 }
221 }
222 }
223
224
225 if not_return_address {
226 for output in tx.output.iter() {
227 if !output.addresses.is_empty(){
228 let out_addr = &output.addresses[0];
229 if address.eq(&out_addr) {
230 possibles_cycles.push(Cycle::new(tx.clone(), out_addr.clone()));
231 }
232 }
233 }
234 }
235
236 }
237 iter.next();
238 }
239 possibles_cycles
240 }
241
242 /// This functions checks if a bitcoin transaction is coinbase and returns 'true' or 'false'.
243 ///
244 /// # Arguments
245 ///
246 /// * `tx` - A 'STransaction' of bitcoin-explorer.
247 ///
248 pub fn is_coinbase(tx: &STransaction) -> bool{
249 tx.input.is_empty()
250 }
251
252 /// Given a 'Db' struct that manage the RocksDB instance with the transactions loaded, a return transaction and target address, validates if the possible cycle is a cycle and returns the path trough the UTXO.
253 ///
254 /// # Arguments
255 ///
256 /// * `db` - A 'Db' struct that manage a RocksDB instance.
257 /// * `tx` - A 'STransactions' of the return transaction.
258 /// * `target_address` - String that defines the target address in the Bitcoin Blockchain.
259 ///
260 /// # Examples
261 /// ```
262 /// use std::path::Path;
263 /// use ext::blockchain::*;
264 /// use ext::db_functions::Db;
265 /// use bitcoin_explorer::BitcoinDB;
266 ///
267 /// let db_b = BitcoinDB::new(Path::new("/home/ubuntu/.bitcoin/"), true).unwrap();
268 /// let num = extract(&db_b, &0, &2000, "./transactions.json");
269 /// let db = Db::new("/tmp/.rocksdb");
270 /// load(&db, "./transactions.json");
271 /// let possible_cycles = search_by_address(&db,"1DZTzaBHUDM7T3QvUKBz4qXMRpkg8jsfB5","35288d269cee1941eaebb2ea85e32b42cdb2b04284a56d8b14dcc3f5c65d6055",&999,&num);
272 /// for cycle in possible_cycles.iter() {
273 /// let r = validate(&db,&mut cycle.get_tx().clone(),&cycle.get_address());
274 /// if !r.is_empty(){
275 /// println!("{:?}",r);
276 /// }
277 /// }
278 /// let _= db.destroy();
279 /// ```
280 ///
281 ///
282 pub fn validate(db: &Db,tx: &mut STransaction,target_address: &Address) -> Vec<STransaction> {
283 let mut visited_tx:Vec<STransaction> = Vec::new();
284
285 while !tx.input.is_empty() {
286 let mut visited_txid:Vec<Txid> = Vec::new();
287 let txc = tx.clone();
288
289 for utxo in txc.input.iter(){
290 if !visited_txid.contains(&utxo.txid){
291 let clave = bincode::serialize(&utxo.txid).unwrap();
292 let res = db.get_tx(&clave);
293 if !res.is_none(){
294 visited_tx.push(txc.clone());
295 visited_txid.push(utxo.txid);
296 let utxo_tx:STransaction = bincode::deserialize(&res.unwrap()).unwrap();
297 let output = &utxo_tx.output[utxo.vout as usize];
298 let out_addr = &output.addresses[0];
299 if out_addr.eq(&target_address) {
300
301 visited_tx.push(utxo_tx);
302 return visited_tx;
303 }else{
304 *tx = utxo_tx;
305 }
306 }else{
307 return vec![];
308 }
309 }
310 }
311 }
312 return vec![];
313 }
314
315}
316
317
318
319