package jelectrum.db;
import java.io.File;
import java.util.Map;
import java.util.Set;
import java.util.HashSet;
import java.util.Collection;
import java.util.LinkedList;
import java.text.DecimalFormat;
import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.StoredBlock;
import org.bitcoinj.core.Block;
import org.bitcoinj.core.NetworkParameters;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.AbstractMap.SimpleEntry;
import static jelectrum.db.ObjectConversionMap.ConversionMode.*;
import jelectrum.SerializedTransaction;
import jelectrum.SerializedBlock;
import jelectrum.UtxoTrieNode;
import jelectrum.Config;
import jelectrum.Util;
import jelectrum.TXUtil;
import jelectrum.TransactionSummary;
import jelectrum.BlockSummary;
import jelectrum.BlockChainCache;
import jelectrum.CacheMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import jelectrum.DaemonThreadFactory;
public abstract class DB implements DBFace
{
protected Config conf;
protected Map<Sha256Hash, SerializedTransaction> tx_map;
protected Map<Sha256Hash, StoredBlock> block_store_map;
protected Map<String, StoredBlock> special_block_store_map;
protected Map<Sha256Hash, SerializedBlock> block_map;
protected Map<Sha256Hash, String> block_rescan_map;
protected Map<String, Object> special_object_map;
protected Map<Integer, String> header_chunk_map;
protected Map<Integer, Sha256Hash> height_map;
protected Map<String, UtxoTrieNode> utxo_trie_map;
protected Map<Sha256Hash, BlockSummary> block_summary_map;
protected DBMapSet address_to_tx_map;
protected DBMapSet tx_to_block_map;
protected NetworkParameters network_params;
protected BlockChainCache block_chain_cache;
protected TXUtil tx_util;
protected int max_set_return_count=100000;
protected Executor exec;
public DB(Config conf)
{
this.conf = conf;
network_params = Util.getNetworkParameters(conf);
tx_util = new TXUtil(this, network_params);
Runtime.getRuntime().addShutdownHook(new DBShutdownThread());
}
public void compact()
{
}
public void commit()
{
}
public void close()
{
}
public void open()
throws Exception
{
tx_map = new ObjectConversionMap<>(SERIALIZEDTRANSACTION, openMap("tx_map"));
block_store_map = new ObjectConversionMap<>(STOREDBLOCK, openMap("block_store_map"), network_params);
special_block_store_map = new ObjectConversionMap<>(STOREDBLOCK, openMap("special_block_store_map"), network_params);
block_map = new ObjectConversionMap<>(OBJECT, openMap("block_map"));
block_rescan_map = new ObjectConversionMap<>(STRING, openMap("block_rescan_map"));
special_object_map = new ObjectConversionMap<>(OBJECT, openMap("special_object_map"));
header_chunk_map = new ObjectConversionMap<>(STRING, openMap("header_chunk_map"));
height_map = new ObjectConversionMap<>(SHA256HASH, openMap("height_map"));
utxo_trie_map = new ObjectConversionMap<>(UTXONODE, openMap("utxo_trie_map"));
//these chazwogers are big an expensive to parse into memory
//so keeping a small cache of them makes sense
block_summary_map = new CacheMap<Sha256Hash,BlockSummary>(32,
new ObjectConversionMap<Sha256Hash,BlockSummary>(OBJECT, openMap("block_summary_map")));
address_to_tx_map = openMapSet("address_to_tx_map");
tx_to_block_map = openMapSet("tx_to_block_map");
}
public TXUtil getTXUtil(){return tx_util;}
protected abstract DBMap openMap(String name) throws Exception;
protected abstract DBMapSet openMapSet(String name) throws Exception;
public Map<Sha256Hash, StoredBlock> getBlockStoreMap(){ return block_store_map; }
public Map<String, StoredBlock> getSpecialBlockStoreMap() { return special_block_store_map; }
public Map<Sha256Hash, SerializedTransaction> getTransactionMap() { return tx_map; }
public Map<Sha256Hash, SerializedBlock> getBlockMap(){ return block_map; }
public Set<Sha256Hash> getTxToBlockMap(Sha256Hash tx) { return tx_to_block_map.getSet(tx.toString(), max_set_return_count); }
public Map<Sha256Hash, String> getBlockRescanMap() { return block_rescan_map; }
public Map<String, Object> getSpecialObjectMap() { return special_object_map; }
public Map<Integer, String> getHeaderChunkMap() {return header_chunk_map; }
public Map<Integer, Sha256Hash> getHeightMap() {return height_map; }
public Map<String, UtxoTrieNode> getUtxoTrieMap() { return utxo_trie_map; }
public Map<Sha256Hash, BlockSummary> getBlockSummaryMap() { return block_summary_map; }
public void addAddressesToTxMap(Collection<String> addresses, Sha256Hash hash)
{
LinkedList<Map.Entry<String, Sha256Hash>> lst = new LinkedList<>();
for(String a : addresses)
{
lst.add(new SimpleEntry<String, Sha256Hash>(a, hash));
}
address_to_tx_map.addAll(lst);
}
public void addAddressesToTxMap(Collection<Map.Entry<String, Sha256Hash> > lst)
{
address_to_tx_map.addAll(lst);
}
public Set<Sha256Hash> getAddressToTxSet(String address)
{
return address_to_tx_map.getSet(address, max_set_return_count);
}
public void addTxToBlockMap(Sha256Hash tx, Sha256Hash block)
{
tx_to_block_map.add(tx.toString(), block);
}
public final void addTxsToBlockMap(Collection<Sha256Hash> txs, Sha256Hash block)
{
LinkedList<Map.Entry<String, Sha256Hash>> lst = new LinkedList<>();
for(Sha256Hash tx : txs)
{
lst.add(new SimpleEntry<String, Sha256Hash>(tx.toString(), block));
}
tx_to_block_map.addAll(lst);
}
public void addTxsToBlockMap(Collection<Map.Entry<Sha256Hash, Sha256Hash> > lst)
{
LinkedList<Map.Entry<String, Sha256Hash>> olst = new LinkedList<>();
for(Map.Entry<Sha256Hash, Sha256Hash> me : lst)
{
olst.add(new SimpleEntry<String, Sha256Hash>(me.getKey().toString(), me.getValue()));
}
tx_to_block_map.addAll(olst);
}
public void addBlockThings(int height, Block blk){}
public boolean needsDetails(){return true;}
public SerializedTransaction getTransaction(Sha256Hash hash)
{
SerializedTransaction stx = getTransactionMap().get(hash);
if (stx != null) return stx;
//ok lets try to get it via block
Set<Sha256Hash> block_hash_set = getTxToBlockMap(hash);
if (block_hash_set == null) return null;
for(Sha256Hash block_hash : block_hash_set)
{
SerializedBlock sb = getBlockMap().get(block_hash);
if (sb != null)
{
Block b = sb.getBlock(network_params);
for(Transaction tx : b.getTransactions())
{
if (tx.getHash().equals(hash))
{
return new SerializedTransaction(tx, b.getTime().getTime());
}
}
}
}
return null;
}
public TransactionSummary getTransactionSummary(Sha256Hash hash)
{
return new TransactionSummary(getTransaction(hash).getTx(network_params), tx_util, null);
}
public void setBlockChainCache(BlockChainCache block_chain_cache)
{
this.block_chain_cache = block_chain_cache;
}
/** Override if the database needs to do soemthing on shutdown */
protected void dbShutdownHandler() throws Exception
{
}
protected synchronized Executor getExec()
{
if (exec == null)
{
exec = new ThreadPoolExecutor(
32,
32,
2, TimeUnit.DAYS,
new LinkedBlockingQueue<Runnable>(),
new DaemonThreadFactory());
}
return exec;
}
public class DBShutdownThread extends Thread
{
public DBShutdownThread()
{
setName("DBShutdownHandler");
}
public void run()
{
try
{
dbShutdownHandler();
}
catch(Throwable t)
{
System.out.println("Exception in DB shutdown: " + t);
t.printStackTrace();
}
}
}
}