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")); } }