Add a CheckpointManager class and a BuildCheckpoints tool that saves a set of checkpoints to disk. By default there is one every difficulty transition period (two weeks), which means a relatively small amount of RAM needed to hold them all. There are only 111 checkpoints so far and old ones can be thinned out if needed.

This commit is contained in:
Mike Hearn
2013-03-04 17:22:17 +01:00
committed by Mike Hearn
parent b4c835c0cf
commit 0419887407
2 changed files with 211 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
package com.google.bitcoin.tools;
import com.google.bitcoin.core.*;
import com.google.bitcoin.store.BlockStore;
import com.google.bitcoin.store.MemoryBlockStore;
import com.google.bitcoin.store.SPVBlockStore;
import com.google.bitcoin.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.prodNet();
// Sorted map of UNIX time of block to StoredBlock object.
final TreeMap<Integer, StoredBlock> checkpoints = new TreeMap<Integer, StoredBlock>();
final BlockStore store = new MemoryBlockStore(params);
final BlockChain chain = new BlockChain(params, store);
final PeerGroup peerGroup = new PeerGroup(params, chain);
peerGroup.addAddress(InetAddress.getLocalHost());
peerGroup.setFastCatchupTimeSecs(new Date().getTime() / 1000);
chain.addListener(new AbstractBlockChainListener() {
@Override
public void notifyNewBestBlock(StoredBlock block) throws VerificationException {
int height = block.getHeight();
if (height % params.interval == 0) {
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("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"));
}
}