Skip to content

Commit

Permalink
CheckpointManager, BuildCheckpoints: remove support for binary checkp…
Browse files Browse the repository at this point in the history
…oint format

Use the textual format instead.
  • Loading branch information
schildbach committed Jun 27, 2024
1 parent 39f409f commit 60c405e
Show file tree
Hide file tree
Showing 2 changed files with 5 additions and 76 deletions.
56 changes: 5 additions & 51 deletions core/src/main/java/org/bitcoinj/core/CheckpointManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,19 +66,18 @@
* different concept of checkpoints that are used to hard-code the validity of blocks that violate BIP30 (duplicate
* coinbase transactions). Those "checkpoints" can be found in NetworkParameters.</p>
*
* <p>The file format consists of the string "CHECKPOINTS 1", followed by a uint32 containing the number of signatures
* <p>The file format is textual, one value per line.
* It consists of the magic string "TXT CHECKPOINTS 1", followed by the number of signatures
* to read. The value may not be larger than 256 (so it could have been a byte but isn't for historical reasons).
* If the number of signatures is larger than zero, each 65 byte ECDSA secp256k1 signature then follows. The signatures
* sign the hash of all bytes that follow the last signature.</p>
*
* <p>After the signatures come an int32 containing the number of checkpoints in the file. Then each checkpoint follows
* one after the other. A checkpoint is 12 bytes for the total work done field, 4 bytes for the height, 80 bytes
* for the block header and then 1 zero byte at the end (i.e. number of transactions in the block: always zero).</p>
* <p>After the signatures come the number of checkpoints in the file. Then each checkpoint follows
* one per line in compact format as a base64-encoded blob.</p>
*/
public class CheckpointManager {
private static final Logger log = LoggerFactory.getLogger(CheckpointManager.class);

private static final String BINARY_MAGIC = "CHECKPOINTS 1";
private static final String TEXTUAL_MAGIC = "TXT CHECKPOINTS 1";
private static final int MAX_SIGNATURES = 256;

Expand All @@ -105,9 +104,7 @@ public CheckpointManager(NetworkParameters params, @Nullable InputStream inputSt
inputStream.mark(1);
int first = inputStream.read();
inputStream.reset();
if (first == BINARY_MAGIC.charAt(0))
dataHash = readBinary(inputStream);
else if (first == TEXTUAL_MAGIC.charAt(0))
if (first == TEXTUAL_MAGIC.charAt(0))
dataHash = readTextual(inputStream);
else
throw new IOException("Unsupported format.");
Expand All @@ -118,49 +115,6 @@ public static InputStream openStream(NetworkParameters params) {
return CheckpointManager.class.getResourceAsStream("/" + params.getId() + ".checkpoints.txt");
}

private Sha256Hash readBinary(InputStream inputStream) throws IOException {
DataInputStream dis = null;
try {
MessageDigest digest = Sha256Hash.newDigest();
DigestInputStream digestInputStream = new DigestInputStream(inputStream, digest);
dis = new DataInputStream(digestInputStream);
digestInputStream.on(false);
byte[] header = new byte[BINARY_MAGIC.length()];
dis.readFully(header);
if (!Arrays.equals(header, BINARY_MAGIC.getBytes(StandardCharsets.US_ASCII)))
throw new IOException("Header bytes did not match expected version");
int numSignatures = dis.readInt();
checkState(numSignatures >= 0 && numSignatures < MAX_SIGNATURES, () ->
"numSignatures out of range: " + numSignatures);
for (int i = 0; i < numSignatures; i++) {
byte[] sig = new byte[65];
dis.readFully(sig);
// TODO: Do something with the signature here.
}
digestInputStream.on(true);
int numCheckpoints = dis.readInt();
checkState(numCheckpoints > 0);
final int size = StoredBlock.COMPACT_SERIALIZED_SIZE;
ByteBuffer buffer = ByteBuffer.allocate(size);
for (int i = 0; i < numCheckpoints; i++) {
if (dis.read(buffer.array(), 0, size) < size)
throw new IOException("Incomplete read whilst loading checkpoints.");
StoredBlock block = StoredBlock.deserializeCompact(buffer);
((Buffer) buffer).position(0);
checkpoints.put(block.getHeader().time(), block);
}
Sha256Hash dataHash = Sha256Hash.wrap(digest.digest());
log.info("Read {} checkpoints up to time {}, hash is {}", checkpoints.size(),
TimeUtils.dateTimeFormat(checkpoints.lastEntry().getKey()), dataHash);
return dataHash;
} catch (ProtocolException e) {
throw new IOException(e);
} finally {
if (dis != null) dis.close();
inputStream.close();
}
}

private Sha256Hash readTextual(InputStream inputStream) throws IOException {
Hasher hasher = Hashing.sha256().newHasher();
try (BufferedReader reader =
Expand Down
25 changes: 0 additions & 25 deletions tools/src/main/java/org/bitcoinj/tools/BuildCheckpoints.java
Original file line number Diff line number Diff line change
Expand Up @@ -158,45 +158,20 @@ public Integer call() throws Exception {

checkState(checkpoints.size() > 0);

final File plainFile = new File("checkpoints" + suffix);
final File textFile = new File("checkpoints" + suffix + ".txt");

// Write checkpoint data out.
writeBinaryCheckpoints(checkpoints, plainFile);
writeTextualCheckpoints(checkpoints, textFile);

peerGroup.stop();
store.close();

// Sanity check the created files.
sanityCheck(plainFile, checkpoints.size());
sanityCheck(textFile, checkpoints.size());

return 0;
}

private static void writeBinaryCheckpoints(TreeMap<Integer, StoredBlock> checkpoints, File file) throws Exception {
MessageDigest digest = Sha256Hash.newDigest();
try (FileOutputStream fileOutputStream = new FileOutputStream(file, false);
DigestOutputStream digestOutputStream = new DigestOutputStream(fileOutputStream, digest);
DataOutputStream dataOutputStream = new DataOutputStream(digestOutputStream)) {
digestOutputStream.on(false);
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) buffer).position(0);
}
Sha256Hash checkpointsHash = Sha256Hash.wrap(digest.digest());
System.out.println("Hash of checkpoints data is " + checkpointsHash);
System.out.println("Checkpoints written to '" + file.getCanonicalPath() + "'.");
}
}

private static void writeTextualCheckpoints(TreeMap<Integer, StoredBlock> checkpoints, File file)
throws IOException {
try (PrintWriter writer = new PrintWriter(
Expand Down

0 comments on commit 60c405e

Please sign in to comment.