package jelectrum;
import org.bitcoinj.core.Sha256Hash;
import java.text.DecimalFormat;
import java.util.Map;
import jelectrum.proto.Blockrepo;
import com.google.protobuf.ByteString;
import org.json.JSONObject;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.CannedAccessControlList;
public class BlockRepoSaver extends Thread
{
private int BLOCKS_PER_CHUNK=100;
private Jelectrum jelly;
private AmazonS3Client s3;
private String bucket;
private int save_after = -1;
public BlockRepoSaver(Jelectrum jelly, int BLOCKS_PER_CHUNK)
{
this.BLOCKS_PER_CHUNK = BLOCKS_PER_CHUNK;
this.jelly = jelly;
setName("BlockRepoSaver/" + BLOCKS_PER_CHUNK);
setDaemon(true);
Config config = jelly.getConfig();
config.require("block_repo_saver_bucket");
config.require("block_repo_saver_aws_id");
config.require("block_repo_saver_aws_key");
s3 = new AmazonS3Client(new BasicAWSCredentials(config.get("block_repo_saver_aws_id"), config.get("block_repo_saver_aws_key")));
bucket = config.get("block_repo_saver_bucket");
if (config.isSet("block_repo_saver_save_after"))
{
save_after = config.getInt("block_repo_saver_save_after");
}
}
public void run()
{
while(true)
{
try
{
doUploadRun();
saveFeeEstimates();
}
catch(Throwable t)
{
jelly.getEventLog().alarm("BlockRepoSaver error: " + t);
t.printStackTrace();
}
try{ sleep(600000); } catch(Throwable t){}
}
}
private void doUploadRun()
{
int head_height = jelly.getElectrumNotifier().getHeadHeight();
Map<String, Object> special_object_map = jelly.getDB().getSpecialObjectMap();
int last_saved=-1;
for(int start=0; start+BLOCKS_PER_CHUNK<=head_height; start+=BLOCKS_PER_CHUNK)
{
int end_block = start + BLOCKS_PER_CHUNK - 1;
Sha256Hash end_hash = jelly.getBlockChainCache().getBlockHashAtHeight(end_block);
String key = "blockchunk/" +BLOCKS_PER_CHUNK+"/" + start;
Sha256Hash db_hash = null;
// Catch up DB
if (start < save_after)
{
continue;
//special_object_map.put(key, end_hash);
}
db_hash = (Sha256Hash) special_object_map.get(key);
if ((db_hash == null) || (!db_hash.equals(end_hash)))
{
Blockrepo.BitcoinBlockPack.Builder pack_builder = Blockrepo.BitcoinBlockPack.newBuilder();
pack_builder.setNewHeadHash(end_hash.toString());
pack_builder.setStartHeight(start);
for(int i=start; i<=end_block; i++)
{
Sha256Hash hash = jelly.getBlockChainCache().getBlockHashAtHeight(i);
SerializedBlock blk = jelly.getDB().getBlockMap().get(hash);
Blockrepo.BitcoinBlock.Builder blk_builder = Blockrepo.BitcoinBlock.newBuilder();
blk_builder.setHeight(i);
blk_builder.setHash(hash.toString());
blk_builder.setBlockData(ByteString.copyFrom(blk.getBytes()));
pack_builder.addBlocks(blk_builder.build());
}
Blockrepo.BitcoinBlockPack pack = pack_builder.build();
ByteString bytes = pack.toByteString();
ByteString c_data = ByteString.copyFrom(lobstack.ZUtil.compress(bytes.toByteArray()));
saveFile(key, c_data, 86400*7);
special_object_map.put(key, end_hash);
jelly.getEventLog().log("BlockRepoSaver"+BLOCKS_PER_CHUNK+" done chunk: " + start + " to " + end_block + " - " + bytes.size() + " / " + c_data.size());
last_saved=end_block;
}
}
if (last_saved > 0)
{
String data_str="" + last_saved;
saveFile("blockchunk/" +BLOCKS_PER_CHUNK+"/max", ByteString.copyFromUtf8(data_str), 300);
}
}
private void saveFeeEstimates()
throws java.io.IOException, org.json.JSONException
{
JSONObject obj = new JSONObject();
obj.put("updated", System.currentTimeMillis());
JSONObject fees = new JSONObject();
for(int i=1; i<120; i++)
{
double fee=jelly.getBitcoinRPC().getFeeEstimate(i);
fees.put("" + i, fee);
}
obj.put("fees", fees);
saveFile("fee_estimates", ByteString.copyFromUtf8(obj.toString(2) +"\n"), 30);
}
private void saveFile(String key, ByteString data, int cache_seconds)
{
ObjectMetadata omd = new ObjectMetadata();
omd.setCacheControl("max-age=" + cache_seconds);
omd.setContentLength(data.size());
PutObjectRequest put = new PutObjectRequest(bucket, key, data.newInput(), omd);
put.setCannedAcl(CannedAccessControlList.PublicRead);
put.setStorageClass(com.amazonaws.services.s3.model.StorageClass.StandardInfrequentAccess.toString());
s3.putObject(put);
}
}