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