// Rust Bitcoin Library // Written in 2014 by // Andrew Poelstra // To the extent possible under law, the author(s) have dedicated all // copyright and related and neighboring rights to this software to // the public domain worldwide. This software is distributed without // any warranty. // // You should have received a copy of the CC0 Public Domain Dedication // along with this software. // If not, see . // //! # Address Index //! //! Maintains an index from addresses to unspent outputs. It reduces size by //! checking that the first byte of HMAC(wallet key, address outscript) is //! zero, so that the index will be 1/256th the size of the utxoset in RAM. //! use std::collections::HashMap; use std::hash::{Hash, Hasher, SipHasher}; use secp256k1::key::SecretKey; use blockdata::transaction::TxOut; use blockdata::transaction::ScriptPubkeyTemplate::PayToPubkeyHash; use blockdata::utxoset::UtxoSet; use blockdata::script::Script; use network::constants::Network; use wallet::address::Address; use wallet::wallet::Wallet; use util::hash::Sha256dHash; /// The type of a wallet-spendable txout #[derive(Clone, PartialEq, Eq, Debug)] pub enum WalletTxOutType { /// Pay-to-address transaction redeemable using an ECDSA key PayToAddress(SecretKey), /// Undetermined Unknown } /// A txout that is spendable by the wallet #[derive(Clone, PartialEq, Eq, Debug)] pub struct WalletTxOut { /// The TXID of the transaction this output is part of pub txid: Sha256dHash, /// The index of the output in its transaction pub vout: u32, /// The blockheight at which this output appeared in the blockchain pub height: u32, /// The actual output pub txo: TxOut, /// A classification of the output pub kind: WalletTxOutType } /// An address index #[derive(Clone, PartialEq, Eq, Debug)] pub struct AddressIndex { tentative_index: HashMap>, index: HashMap<(Sha256dHash, u32), Vec>, network: Network, k1: u64, k2: u64 } impl AddressIndex { /// Creates a new address index from a wallet (which provides an authenticated /// hash function for prefix filtering) and UTXO set (which is what gets filtered). pub fn new(utxo_set: &UtxoSet, wallet: &Wallet) -> AddressIndex { let (k1, k2) = wallet.siphash_key(); let mut ret = AddressIndex { tentative_index: HashMap::with_capacity(utxo_set.n_utxos() / 256), index: HashMap::new(), network: wallet.network(), k1: k1, k2: k2 }; for (key, idx, txo, height) in utxo_set.iter() { if ret.admissible_txo(txo) { let new = WalletTxOut { txid: key, vout: idx, height: height, txo: txo.clone(), kind: WalletTxOutType::Unknown }; let entry = ret.tentative_index.entry(txo.script_pubkey.clone()); let txos = entry.or_insert(vec![]); txos.push(new); } } ret } /// #[inline] pub fn index_wallet_txo(&mut self, wtx: &WalletTxOut, kind: WalletTxOutType) { let mut new = wtx.clone(); new.kind = kind; let entry = self.index.entry((wtx.txid, wtx.vout)); let txos = entry.or_insert(vec![]); txos.push(new); } /// A filtering function used for creating a small address index. #[inline] pub fn admissible_address(&self, addr: &Address) -> bool { let mut hasher = SipHasher::new_with_keys(self.k1, self.k2); (&addr[..]).hash(&mut hasher); hasher.finish() & 0xFF == 0 } /// A filtering function used for creating a small address index. #[inline] pub fn admissible_txo(&self, out: &TxOut) -> bool { match out.classify(self.network) { PayToPubkeyHash(addr) => self.admissible_address(&addr), _ => false } } /// Lookup a txout by its scriptpubkey. Returns a slice because there /// may be more than one for any given scriptpubkey. #[inline] pub fn find_by_script<'a>(&'a self, pubkey: &Script) -> &'a [WalletTxOut] { self.tentative_index.get(pubkey).map(|v| &v[..]).unwrap_or(&[]) } }