package jelectrum;
import org.bitcoinj.store.H2FullPrunedBlockStore;
import org.bitcoinj.store.BlockStore;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.BlockChain;
import org.bitcoinj.core.PeerGroup;
import org.bitcoinj.core.PeerAddress;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.core.VersionMessage;
import org.bitcoinj.core.Peer;
import org.apache.commons.codec.binary.Hex;
import java.net.InetAddress;
import org.bitcoinj.net.discovery.DnsDiscovery;
import java.util.LinkedList;
import jelectrum.db.DBFace;
public class Jelectrum
{
public static void main(String args[]) throws Exception
{
new Jelectrum(new Config(args[0])).start();
}
private Config config;
private DBFace jelectrum_db;
private Importer importer;
private MapBlockStore block_store;
private BlockChain block_chain;
private BlockChainCache block_chain_cache;
private PeerGroup peer_group;
private EventLog event_log;
private StratumServer stratum_server;
private NetworkParameters network_params;
private ElectrumNotifier notifier;
private HeaderChunkAgent header_chunk_agent;
private BitcoinRPC bitcoin_rpc;
private UtxoTrieMgr utxo_trie_mgr;
private PeerManager peer_manager;
private volatile boolean caught_up=false;
public Jelectrum(Config conf)
throws Exception
{
config = conf;
network_params = Util.getNetworkParameters(config);
config.require("bitcoin_network_use_peers");
config.require("db_type");
//config.require("bitcoin_peer_host");
//config.require("bitcoin_peer_port");
event_log = new EventLog(config);
String db_type = config.get("db_type");
//jelectrum_db = new JelectrumDBMapDB(config);
//jelectrum_db = new JelectrumDBDirect(config);
//jelectrum_db = new JelectrumDBCloudData(config);
if (config.getBoolean("bitcoind_enable"))
{
bitcoin_rpc = new BitcoinRPC(config);
bitcoin_rpc.testConnection();
}
if (db_type.equals("mongo"))
{
jelectrum_db = new jelectrum.db.mongo.MongoDB(config);
}
/*else if (db_type.equals("sql"))
{
jelectrum_db = new JelectrumDBSQL(config);
}*/
else if (db_type.equals("leveldb"))
{
jelectrum_db = new jelectrum.db.level.LevelDB(event_log, config);
}
else if (db_type.equals("slopbucket"))
{
jelectrum_db = new jelectrum.db.slopbucket.SlopbucketDB(config, event_log);
}
else if (db_type.equals("lobstack"))
{
jelectrum_db = new jelectrum.db.lobstack.LobstackDB(this, config);
}
else if (db_type.equals("lmdb"))
{
jelectrum_db = new jelectrum.db.lmdb.LMDB(config);
}
else if (db_type.equals("little"))
{
jelectrum_db = new jelectrum.db.little.LittleDB(config, event_log, network_params);
}
else if (db_type.equals("memory"))
{
jelectrum_db = new jelectrum.db.memory.MemoryDB(config);
}
else if (db_type.equals("redis"))
{
jelectrum_db = new jelectrum.db.jedis.JedisDB(config);
}
else if (db_type.equals("rocksdb"))
{
jelectrum_db = new jelectrum.db.rocksdb.JRocksDB(config, event_log);
}
else if (db_type.equals("cassandra"))
{
jelectrum_db = new jelectrum.db.cassandra.CassandraDB(config);
}
else
{
System.out.println("Unknown db_type: " + db_type);
System.out.println("Try mongo or sql or leveldb or lobstack or slopbucket or rocksdb");
System.exit(-1);
}
block_store = new MapBlockStore(this);
block_chain = new BlockChain(network_params, block_store);
utxo_trie_mgr = new UtxoTrieMgr(this);
notifier = new ElectrumNotifier(this);
importer = new Importer(network_params, this, block_store);
peer_manager = new PeerManager(this);
stratum_server = new StratumServer(this, config);
block_chain_cache = BlockChainCache.load(this);
//block_chain_cache.undumbSelf(network_params, block_store);
header_chunk_agent = new HeaderChunkAgent(this);
jelectrum_db.setBlockChainCache(block_chain_cache);
}
public void start()
throws Exception
{
utxo_trie_mgr.getUtxoState();
if (config.getBoolean("bulk_import_enabled"))
{
new BulkImporter(this);
}
System.out.println("Updating block chain cache");
block_chain_cache.update(this, block_store.getChainHead());
utxo_trie_mgr.start();
System.out.println("Starting things");
importer.start();
notifier.start();
stratum_server.setEventLog(event_log);
stratum_server.start();
peer_manager.start();
System.out.println("Starting bitcoin peer download");
peer_group = new PeerGroup(network_params, block_chain);
peer_group.setMaxPeersToDiscoverCount(256);
peer_group.setUseLocalhostPeerWhenPossible(false);
peer_group.setMaxConnections(60);
if (config.isSet("bitcoin_peer_host") && (config.isSet("bitcoin_peer_port")))
{
peer_group.addAddress(
new PeerAddress(
InetAddress.getByName(
config.get("bitcoin_peer_host")),
config.getInt("bitcoin_peer_port")));
}
if (config.isSet("bitcoin_peer_list"))
{
for(String peer : config.getList("bitcoin_peer_list"))
{
event_log.log("Adding additional bitcoin peer: " + peer);
peer_group.addAddress(new PeerAddress(InetAddress.getByName(peer),8333));
}
}
if (config.getBoolean("bitcoin_network_use_peers"))
{
peer_group.addPeerDiscovery(new DnsDiscovery(network_params));
}
peer_group.addDataEventListener(new ImportEventListener(importer));
peer_group.addOnTransactionBroadcastListener(new ImportEventListener(importer));
peer_group.setMinBroadcastConnections(1);
peer_group.waitForPeers(1);
peer_group.start();
Thread.sleep(2500);
while (peer_group.numConnectedPeers() == 0)
{
event_log.alarm("No connected bitcoin peers - can't get new blocks or transactions");
Thread.sleep(5000);
}
event_log.log("Connected bitcoin peers: " + peer_group.getConnectedPeers().size());
for(Peer p : peer_group.getConnectedPeers())
{
event_log.log("Connected bitcoin peer: " + p);
}
event_log.log("Pending bitcoin peers: " + peer_group.getPendingPeers().size());
for(Peer p : peer_group.getPendingPeers())
{
event_log.log("Pending bitcoin peer: " + p);
}
peer_group.downloadBlockChain();
while(peer_group.getMostCommonChainHeight() > notifier.getHeadHeight())
{
Thread.sleep(5000);
}
System.out.println("Block chain caught up");
event_log.log("Block chain caught up");
caught_up=true;
new IrcBot(this,null).start();
new IrcBot(this,"onion").start();
importer.setBlockPrintEvery(1);
importer.disableRatePrinting();
header_chunk_agent.start();
if (config.getBoolean("block_repo_saver"))
{
new BlockRepoSaver(this,100).start();
new BlockRepoSaver(this,10).start();
}
while(true)
{
int peer_height = peer_group.getMostCommonChainHeight();
int my_height = notifier.getHeadHeight();
/*if (peer_height != my_height)
{
event_log.log("Peer height is: " + peer_height + " My height is: " + my_height);
for(Peer p : peer_group.getConnectedPeers())
{
StringBuilder sb = new StringBuilder();
VersionMessage vm = p.getPeerVersionMessage();
sb.append("Peer:");
sb.append(" " + vm.theirAddr);
sb.append(" Height: " + p.getBestHeight());
event_log.log(sb.toString());
}
}*/
if (peer_group.getMostCommonChainHeight() > notifier.getHeadHeight()+3)
{
event_log.alarm("We are far behind. Aborting.");
System.exit(1);
}
Thread.sleep(25000);
}
/*peer_group.stop();
jelectrum_db.commit();
jelectrum_db.close();*/
}
public boolean isUpToDate()
{
return caught_up;
}
public Config getConfig()
{
return config;
}
public Importer getImporter()
{
return importer;
}
public DBFace getDB()
{
return jelectrum_db;
}
public NetworkParameters getNetworkParameters()
{
return network_params;
}
public ElectrumNotifier getElectrumNotifier()
{
return notifier;
}
public MapBlockStore getBlockStore()
{
return block_store;
}
public EventLog getEventLog()
{
return event_log;
}
public PeerGroup getPeerGroup()
{
return peer_group;
}
public BlockChainCache getBlockChainCache()
{
return block_chain_cache;
}
public HeaderChunkAgent getHeaderChunkAgent()
{
return header_chunk_agent;
}
public BitcoinRPC getBitcoinRPC()
{
return bitcoin_rpc;
}
public UtxoTrieMgr getUtxoTrieMgr()
{
return utxo_trie_mgr;
}
public StratumServer getStratumServer()
{
return stratum_server;
}
public PeerManager getPeerManager()
{
return peer_manager;
}
private volatile boolean space_limited;
public void setSpaceLimited(boolean limited)
{
if (limited)
{
event_log.alarm("Going into space limited mode");
}
else
{
event_log.alarm("Returning from space limited mode");
}
space_limited = limited;
}
public boolean getSpaceLimited()
{
return space_limited;
}
}