package jelectrum.db.little;
import jelectrum.db.DB;
import jelectrum.db.DBMap;
import jelectrum.db.DBMapSet;
import jelectrum.db.mongo.MongoDB;
import jelectrum.db.level.LevelDB;
import jelectrum.db.lmdb.LMDB;
import slopbucket.Slopbucket;
import jelectrum.Config;
import jelectrum.EventLog;
import jelectrum.BloomLayerCake;
import jelectrum.SerializedTransaction;
import jelectrum.BitcoinRPC;
import jelectrum.TXUtil;
import jelectrum.TimeRecord;
import jelectrum.BlockChainCache;
import jelectrum.SerializedBlock;
import jelectrum.TransactionSummary;
import jelectrum.BlockSummary;
import java.io.File;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;
import java.util.HashMap;
import com.google.protobuf.ByteString;
import org.bitcoinj.core.Block;
import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.NetworkParameters;
import jelectrum.db.ObjectConversionMap;
import static jelectrum.db.ObjectConversionMap.ConversionMode.*;
import jelectrum.db.slopbucket.SlopbucketMap;
import org.apache.commons.codec.binary.Hex;
import org.junit.Assert;
public class LittleDB extends LMDB
{
private BloomLayerCake cake;
private TXUtil tx_util;
private NetworkParameters network_parameters;
private HashMap<Sha256Hash, TransactionSummary> import_tx_summary_cache;
private boolean debug=false;
public LittleDB(Config conf, EventLog log, NetworkParameters network_parameters)
throws Exception
{
super(conf);
this.network_parameters = network_parameters;
import_tx_summary_cache = new HashMap<>();
tx_util = new TXUtil(this, network_params);
conf.require("little_path");
File dir = new File(conf.get("little_path"));
dir.mkdirs();
File cake_dir = new File(dir, "bloom");
cake_dir.mkdirs();
cake = new BloomLayerCake(cake_dir);
tx_map = null;
if (conf.getBoolean("utxo_disable"))
{
utxo_trie_map = null;
}
/*{
Sha256Hash h = new Sha256Hash("0000000000000000083fb1d19f7b2feb889a49f0dc25a5c60e5783f3cd4a734d");
BlockSummary bs = getBlockSummaryMap().get(h);
jelectrum.proto.Summary.BitcoinBlockSummary sum = bs.getProto();
System.out.println("Block summary size protobuf: " + sum.toByteString().size());
byte[] comp = lobstack.ZUtil.compress(sum.toByteString().toByteArray());
System.out.println("Compressed protobuf: " + comp.length);
{
java.io.ByteArrayOutputStream b_out = new java.io.ByteArrayOutputStream();
java.io.ObjectOutputStream o_steam = new java.io.ObjectOutputStream(b_out);
o_steam.writeObject(bs);
byte[] uncom = b_out.toByteArray();//Array();
comp = lobstack.ZUtil.compress(uncom);
System.out.println("Compressed object: " + comp.length);
}
getBlockSummaryMap().put(h, bs);
System.out.println(bs.toString());
}*/
}
@Override
public DBMapSet openMapSet(String name) {return null;}
@Override
public void addBlockThings(int height, Block b)
{
//System.out.println("Adding block " + height + " " + b.getHash());
//Make my own copy, which is needed for working in a single block (from importer)
HashMap<Sha256Hash, TransactionSummary> import_block_cache = new HashMap<Sha256Hash, TransactionSummary>();
BlockSummary block_summary = new BlockSummary(height, b, tx_util, import_block_cache);
// Put everything in the shared cache for other threads, which hit it in getTransactionSummary below
synchronized(import_tx_summary_cache)
{
import_tx_summary_cache.putAll(import_block_cache);
}
getBlockSummaryMap().put(b.getHash(), block_summary);
long t1=System.nanoTime();
cake.addAddresses(height, block_summary.getAllAddresses());
TimeRecord.record(t1, "cake_add_addresses");
}
@Override
public synchronized void commit()
{
cake.flush();
synchronized(import_tx_summary_cache)
{
import_tx_summary_cache.clear();
}
}
@Override
public boolean needsDetails(){return false;}
@Override
public Set<Sha256Hash> getAddressToTxSet(String address)
{
long t1=System.nanoTime();
Set<Integer> heights = cake.getBlockHeightsForAddress(address);
TimeRecord.record(t1, "bloom_cake_get_blocks_for_addr");
Set<Sha256Hash> out_list = new HashSet<Sha256Hash>();
for(int height : heights)
{
Sha256Hash b = block_chain_cache.getBlockHashAtHeight(height);
if (b != null)
{
long t1_bs=System.nanoTime();
BlockSummary bs = getBlockSummaryMap().get(b);
TimeRecord.record(t1_bs,"db_get_block_summary");
out_list.addAll(bs.getMatchingTransactions(address));
}
}
TimeRecord.record(t1,"db_get_addr_to_tx");
return out_list;
}
@Override
public Set<Sha256Hash> getTxToBlockMap(Sha256Hash tx)
{
long t1=System.nanoTime();
Set<Integer> heights = cake.getBlockHeightsForAddress(tx.toString());
TimeRecord.record(t1, "bloom_cake_get_blocks_for_tx");
Set<Sha256Hash> blocks = new HashSet<Sha256Hash>();
for(int height : heights)
{
//System.out.println("Height: " + height);
Sha256Hash b = block_chain_cache.getBlockHashAtHeight(height);
if (debug)
{
if (b == null) System.out.println("Finding: " + tx + " no hash found for height: " + height + " head is: " + block_chain_cache.getHead());
}
if (b != null)
{
Assert.assertNotNull(b);
long t1_bs=System.nanoTime();
BlockSummary bs = getBlockSummaryMap().get(b);
TimeRecord.record(t1_bs,"db_get_block_summary");
TransactionSummary ts = bs.getTxMap().get(tx);
if (ts != null) blocks.add(b);
}
}
TimeRecord.record(t1, "db_get_tx_to_block");
return blocks;
}
@Override
public TransactionSummary getTransactionSummary(Sha256Hash hash)
{
if (debug) System.out.println("Looking up tx summary: " + hash);
long t1=System.nanoTime();
synchronized(import_tx_summary_cache)
{
if (import_tx_summary_cache.containsKey(hash))
{
TimeRecord.record(t1, "db_get_txsummary_cached");
return import_tx_summary_cache.get(hash);
}
}
Set<Sha256Hash> block_list = getTxToBlockMap(hash);
if (debug) System.out.println("Block list: " + block_list);
for(Sha256Hash block_hash : block_list)
{
BlockSummary bs = getBlockSummaryMap().get(block_hash);
TransactionSummary ts = bs.getTxMap().get(hash);
TimeRecord.record(t1, "db_get_txsummary_loaded");
return ts;
}
TimeRecord.record(t1, "db_get_txsummary_notfound");
return null;
}
@Override
public SerializedTransaction getTransaction(Sha256Hash hash)
{
if (debug) System.out.println("Looking up tx: " + hash);
long t1=System.nanoTime();
Set<Sha256Hash> block_list = getTxToBlockMap(hash);
//System.out.println("Get tx: " + hash + " - blocks: " + block_list);
for(Sha256Hash block_hash : block_list)
{
SerializedBlock sb = getBlockMap().get(block_hash);
if (sb != null)
{
Block b = sb.getBlock(network_parameters);
for(Transaction tx : b.getTransactions())
{
if (tx.getHash().equals(hash))
{
TimeRecord.record(t1, "db_get_tx_found");
return new SerializedTransaction(tx, b.getTime().getTime());
}
}
}
}
TimeRecord.record(t1, "db_get_tx_not_found");
return null;
}
}