use bitcoin::{Block, Txid, Script};
use super::super::*;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct AddressIndexDBKey {
pub script_pubkey: Script,
pub txid: Txid,
}
impl Serialize for AddressIndexDBKey {
fn serialize(&self) -> Vec<u8> {
[consensus_encode(&self.script_pubkey), consensus_encode(&self.txid)].concat()
}
}
impl Deserialize for AddressIndexDBKey {
fn deserialize(buf: &[u8]) -> Self {
let script_pubkey = consensus_decode(&buf[0..buf.len()-32]);
let txid = consensus_decode(&buf[buf.len()-32..]);
AddressIndexDBKey {
script_pubkey,
txid,
}
}
}
pub type AddressIndexDBValue = Empty;
#[derive(Debug)]
pub struct AddressIndexDB {
db: RocksDB<AddressIndexDBKey, AddressIndexDBValue>,
}
impl AddressIndexDB {
pub fn get_path(coin: &str) -> String {
format!("{}/{}/address_index", data_dir(), coin)
}
pub fn new(coin: &str, temporary: bool) -> Self {
let path = Self::get_path(coin);
Self {
db: RocksDB::new(&path, temporary),
}
}
pub fn get(&self, script_pubkey: &Script) -> Vec<Txid> {
let script_pubkey = consensus_encode(script_pubkey);
self.db.prefix_iter(script_pubkey).map(|(key, _value)| key.txid).collect()
}
pub fn put(&self, script_pubkey: &Script, txid: &Txid) {
let key = AddressIndexDBKey {
script_pubkey: (*script_pubkey).clone(),
txid: *txid,
};
self.db.put(&key, &Default::default());
}
pub fn process_block(&self, block: &Block, previous_utxos: &[UtxoEntry]) {
let mut previous_utxo_index = 0;
for tx in block.txdata.iter() {
let txid = tx.txid();
for vin in tx.input.iter() {
if !vin.previous_output.is_null() {
self.put(&previous_utxos[previous_utxo_index].script_pubkey, &txid);
previous_utxo_index += 1;
}
}
for vout in tx.output.iter() {
self.put(&vout.script_pubkey, &txid);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[allow(dead_code)]
fn print_addr_index_db(addr_index_db: &AddressIndexDB) {
let mut entries = addr_index_db.db.iter().map(|(key, _value)| key).collect::<Vec<AddressIndexDBKey>>();
entries.sort();
for entry in entries.iter() {
println!(" AddressIndexDBKey {{ script_pubkey: consensus_decode(&hex::decode(\"{}\").unwrap()), txid: consensus_decode(&hex::decode(\"{}\").unwrap()), }},",
hex::encode(consensus_encode(&entry.script_pubkey)),
hex::encode(consensus_encode(&entry.txid)));
}
}
#[test]
fn addr_index_db() {
let addr_index_db = AddressIndexDB::new("test/address_index", true);
let mut utxo_db = UtxoDB::new("test/address_index", true);
for block in fixtures::regtest_blocks().iter() {
let prev_utxos = utxo_db.process_block(&block, false);
addr_index_db.process_block(&block, &prev_utxos);
}
print_addr_index_db(&addr_index_db);
let mut entries_test = addr_index_db.db.iter().map(|(key, _value)| key).collect::<Vec<AddressIndexDBKey>>();
entries_test.sort();
let mut entries = fixtures::addr_index_db();
entries.sort();
assert_eq!(entries_test, entries);
for entry in entries.iter() {
let mut found = false;
for txid in addr_index_db.get(&entry.script_pubkey).iter() {
if *txid == entry.txid {
found = true;
break;
}
}
assert!(found);
}
}
}