chainseeker_server/db/
address_index.rs

1use bitcoin::{Block, Txid, Script};
2
3use super::super::*;
4
5#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
6pub struct AddressIndexDBKey {
7    pub script_pubkey: Script,
8    pub txid: Txid,
9}
10
11impl Serialize for AddressIndexDBKey {
12    fn serialize(&self) -> Vec<u8> {
13        [consensus_encode(&self.script_pubkey), consensus_encode(&self.txid)].concat()
14    }
15}
16
17impl Deserialize for AddressIndexDBKey {
18    fn deserialize(buf: &[u8]) -> Self {
19        let script_pubkey = consensus_decode(&buf[0..buf.len()-32]);
20        let txid = consensus_decode(&buf[buf.len()-32..]);
21        AddressIndexDBKey {
22            script_pubkey,
23            txid,
24        }
25    }
26}
27
28pub type AddressIndexDBValue = Empty;
29
30#[derive(Debug)]
31pub struct AddressIndexDB {
32    db: RocksDB<AddressIndexDBKey, AddressIndexDBValue>,
33}
34
35/// The database which stores (script_pubkey, txid) tuple.
36impl AddressIndexDB {
37    pub fn get_path(coin: &str) -> String {
38        format!("{}/{}/address_index", data_dir(), coin)
39    }
40    pub fn new(coin: &str, temporary: bool) -> Self {
41        let path = Self::get_path(coin);
42        Self {
43            db: RocksDB::new(&path, temporary),
44        }
45    }
46    pub fn get(&self, script_pubkey: &Script) -> Vec<Txid> {
47        let script_pubkey = consensus_encode(script_pubkey);
48        self.db.prefix_iter(script_pubkey).map(|(key, _value)| key.txid).collect()
49    }
50    pub fn put(&self, script_pubkey: &Script, txid: &Txid) {
51        let key = AddressIndexDBKey {
52            script_pubkey: (*script_pubkey).clone(),
53            txid: *txid,
54        };
55        self.db.put(&key, &Default::default());
56    }
57    pub fn process_block(&self, block: &Block, previous_utxos: &[UtxoEntry]) {
58        let mut previous_utxo_index = 0;
59        for tx in block.txdata.iter() {
60            let txid = tx.txid();
61            // Process vins.
62            for vin in tx.input.iter() {
63                if !vin.previous_output.is_null() {
64                    // Fetch transaction from `previous_output`.
65                    self.put(&previous_utxos[previous_utxo_index].script_pubkey, &txid);
66                    previous_utxo_index += 1;
67                }
68            }
69            // Process vouts.
70            for vout in tx.output.iter() {
71                self.put(&vout.script_pubkey, &txid);
72            }
73        }
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80    #[allow(dead_code)]
81    fn print_addr_index_db(addr_index_db: &AddressIndexDB) {
82        let mut entries = addr_index_db.db.iter().map(|(key, _value)| key).collect::<Vec<AddressIndexDBKey>>();
83        entries.sort();
84        for entry in entries.iter() {
85            println!("        AddressIndexDBKey {{ script_pubkey: consensus_decode(&hex::decode(\"{}\").unwrap()), txid: consensus_decode(&hex::decode(\"{}\").unwrap()), }},",
86                hex::encode(consensus_encode(&entry.script_pubkey)),
87                hex::encode(consensus_encode(&entry.txid)));
88        }
89    }
90    #[test]
91    fn addr_index_db() {
92        let addr_index_db = AddressIndexDB::new("test/address_index", true);
93        let mut utxo_db = UtxoDB::new("test/address_index", true);
94        for block in fixtures::regtest_blocks().iter() {
95            let prev_utxos = utxo_db.process_block(&block, false);
96            addr_index_db.process_block(&block, &prev_utxos);
97        }
98        print_addr_index_db(&addr_index_db);
99        let mut entries_test = addr_index_db.db.iter().map(|(key, _value)| key).collect::<Vec<AddressIndexDBKey>>();
100        entries_test.sort();
101        let mut entries = fixtures::addr_index_db();
102        entries.sort();
103        assert_eq!(entries_test, entries);
104        for entry in entries.iter() {
105            let mut found = false;
106            for txid in addr_index_db.get(&entry.script_pubkey).iter() {
107                if *txid == entry.txid {
108                    found = true;
109                    break;
110                }
111            }
112            assert!(found);
113        }
114    }
115}