package com.google.digitalcoin.tools;
import com.google.digitalcoin.core.*;
import com.google.digitalcoin.store.BlockStore;
import com.google.digitalcoin.store.MemoryBlockStore;
import com.google.digitalcoin.utils.BriefLogFormatter;
import java.io.*;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.util.Date;
import java.util.TreeMap;
import static com.google.common.base.Preconditions.checkState;
/**
* Downloads and verifies a full chain from your local peer, emitting checkpoints at each difficulty transition period
* to a file which is then signed with your key.
*/
public class BuildCheckpoints {
public static void main(String[] args) throws Exception {
BriefLogFormatter.init();
final NetworkParameters params = NetworkParameters.prodNetHank();
// Sorted map of UNIX time of block to StoredBlock object.
final TreeMap<Integer, StoredBlock> checkpoints = new TreeMap<Integer, StoredBlock>();
// Configure digitalcoinj to fetch only headers, not save them to disk, connect to a local fully synced/validated
// node and to save block headers that are on interval boundaries, as long as they are before the time boundary
final BlockStore store = new MemoryBlockStore(params);
final BlockChain chain = new BlockChain(params, store);
final PeerGroup peerGroup = new PeerGroup(params, chain);
peerGroup.addAddress(InetAddress.getLocalHost());
long now = new Date().getTime() / 1000;
peerGroup.setFastCatchupTimeSecs(now);
final long newest_limit = now - (86400 * 2);
chain.addListener(new AbstractBlockChainListener() {
@Override
public void notifyNewBestBlock(StoredBlock block) throws VerificationException {
int height = block.getHeight();
int nDifficultySwitchHeight = 33000;
boolean fNewDifficultyProtocol = (height >= nDifficultySwitchHeight);
int nTargetTimespanCurrent = fNewDifficultyProtocol ? params.targetTimespan : (params.targetTimespan*4);
int interval = nTargetTimespanCurrent/params.targetSpacing;
if ((height == nDifficultySwitchHeight || height % interval == 0) && block.getHeader().getTimeSeconds() <= newest_limit) {
System.out.println(String.format("Checkpointing block %s at height %d",
block.getHeader().getHash(), block.getHeight()));
checkpoints.put(height, block);
}
}
});
peerGroup.startAndWait();
peerGroup.downloadBlockChain();
checkState(checkpoints.size() > 0);
// Write checkpoint data out.
final FileOutputStream fileOutputStream = new FileOutputStream("digitalcoin-checkpoints", false);
MessageDigest digest = MessageDigest.getInstance("SHA-256");
final DigestOutputStream digestOutputStream = new DigestOutputStream(fileOutputStream, digest);
digestOutputStream.on(false);
final DataOutputStream dataOutputStream = new DataOutputStream(digestOutputStream);
dataOutputStream.writeBytes("CHECKPOINTS 1");
dataOutputStream.writeInt(0); // Number of signatures to read. Do this later.
digestOutputStream.on(true);
dataOutputStream.writeInt(checkpoints.size());
ByteBuffer buffer = ByteBuffer.allocate(StoredBlock.COMPACT_SERIALIZED_SIZE);
for (StoredBlock block : checkpoints.values()) {
block.serializeCompact(buffer);
dataOutputStream.write(buffer.array());
buffer.position(0);
}
dataOutputStream.close();
Sha256Hash checkpointsHash = new Sha256Hash(digest.digest());
System.out.println("Hash of checkpoints data is " + checkpointsHash);
digestOutputStream.close();
fileOutputStream.close();
peerGroup.stopAndWait();
store.close();
// Sanity check the created file.
CheckpointManager manager = new CheckpointManager(params, new FileInputStream("checkpoints"));
checkState(manager.numCheckpoints() == checkpoints.size());
StoredBlock test = manager.getCheckpointBefore(1348310800); // Just after block 200,000
checkState(test.getHeight() == 199584);
checkState(test.getHeader().getHashAsString().equals("000000000000002e00a243fe9aa49c78f573091d17372c2ae0ae5e0f24f55b52"));
}
}