package jelectrum;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.ArrayList;
import org.bitcoinj.core.StoredBlock;
import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.core.Block;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.store.BlockStore;
public class BlockChainCache
{
private HashMap<Integer, Sha256Hash> height_map;
private HashSet<Sha256Hash> main_chain;
private volatile Sha256Hash head;
private transient Object update_lock=new Object();
public static final int HEIGHT_BUCKETS=8;
public static final int UPDATES_BEFORE_SAVE=5;
private transient int updates = 0;
public BlockChainCache()
{
height_map = new HashMap<Integer, Sha256Hash>(500000, 0.75f);
main_chain = new HashSet<Sha256Hash>(500000, 0.75f);
head=null;
}
public BlockChainCache(ArrayList<HashMap<Integer, Sha256Hash> > lst)
{
this();
for(HashMap<Integer, Sha256Hash> m : lst)
{
height_map.putAll(m);
main_chain.addAll(m.values());
}
int max_height = -1;
for(Integer i : height_map.keySet())
{
max_height = Math.max(max_height, i);
}
if (max_height >= 0)
head = height_map.get(max_height);
}
private void retransient()
{
update_lock = new Object();
}
public void undumbSelf(NetworkParameters params, BlockStore block_store)
throws org.bitcoinj.store.BlockStoreException
{
Sha256Hash genesis_hash = params.getGenesisBlock().getHash();
StoredBlock cur = block_store.get(head);
synchronized(update_lock)
{
while(true)
{
int height = cur.getHeight();
if (!height_map.containsKey(height))
{
System.out.println("Height map missing: " + height);
height_map.put(height, cur.getHeader().getHash());
}
if (main_chain.contains(cur.getHeader().getHash()))
{
System.out.println("Main chain missing: " + height);
main_chain.add(cur.getHeader().getHash());
}
if (cur.getHeader().getHash().equals(genesis_hash)) return;
cur = cur.getPrev(block_store);
}
}
}
public void update(Jelectrum jelly, StoredBlock new_head)
throws org.bitcoinj.store.BlockStoreException
{
System.out.println("chain update, new head: " + new_head.getHeader().getHash() + " - " + new_head.getHeight());
if (new_head.getHeader().getHash().equals(head)) return;
Sha256Hash genesis_hash = jelly.getNetworkParameters().getGenesisBlock().getHash();
synchronized(update_lock)
{
StoredBlock blk = new_head;
while(true)
{
synchronized(this)
{
int height = blk.getHeight();
Sha256Hash old = height_map.put(height, blk.getHeader().getHash());
if ((old!=null) && (old.equals(blk.getHeader().getHash())))
{
break;
}
if (old!=null)
{
main_chain.remove(old);
}
main_chain.add(blk.getHeader().getHash());
updates++;
}
if (blk.getHeader().getHash().equals(genesis_hash)) break;
blk = blk.getPrev(jelly.getBlockStore());
}
head = new_head.getHeader().getHash();
if (updates >= UPDATES_BEFORE_SAVE)
{
updates=0;
save(jelly);
}
}
}
private void save(Jelectrum jelly)
{
//jelly.getDB().getSpecialObjectMap().put("BlockChainCache", this);
/*
ArrayList<HashMap<Integer, Sha256Hash> > height_map_list = new ArrayList<HashMap<Integer, Sha256Hash> >();
for(int i=0; i<HEIGHT_BUCKETS; i++)
{
height_map_list.add(new HashMap<Integer, Sha256Hash>(height_map.size() * 2 / HEIGHT_BUCKETS + 1,0.5f));
}
for(Map.Entry<Integer, Sha256Hash> me : height_map.entrySet())
{
int h = me.getKey();
Sha256Hash hash = me.getValue();
int idx = h % HEIGHT_BUCKETS;
height_map_list.get(idx).put(h, hash);
}
for(int i=0; i<HEIGHT_BUCKETS; i++)
{
jelly.getDB().getSpecialObjectMap().put("BlockChainCache_" + i, height_map_list.get(i));
}*/
}
public static BlockChainCache load(Jelectrum jelly)
{
return new BlockChainCache();
/*try
{
ArrayList<HashMap<Integer, Sha256Hash> > height_map_list = new ArrayList<HashMap<Integer, Sha256Hash> >();
for(int i=0; i<HEIGHT_BUCKETS; i++)
{
HashMap<Integer, Sha256Hash> m = (HashMap<Integer, Sha256Hash>) jelly.getDB().getSpecialObjectMap().get("BlockChainCache_" + i);
height_map_list.add(m);
}
BlockChainCache c = new BlockChainCache(height_map_list);
return c;
}
catch(Throwable t)
{
System.out.println("Error loading BlockChainCache. Creating new.");
return new BlockChainCache();
}*/
}
public synchronized Sha256Hash getBlockHashAtHeight(int height)
{
return height_map.get(height);
}
public synchronized boolean isBlockInMainChain(Sha256Hash hash)
{
return main_chain.contains(hash);
}
public synchronized Sha256Hash getHead()
{
return head;
}
}