From b4a4137a7669e9d99ad7352c84bf3f2b0e2dfccf Mon Sep 17 00:00:00 2001 From: Andreas Schildbach Date: Thu, 27 Jun 2024 19:37:26 +0200 Subject: [PATCH] CheckpointManager, BuildCheckpoints: remove support for binary checkpoint format Use the textual format instead. --- .../org/bitcoinj/core/CheckpointManager.java | 56 ++----------------- .../org/bitcoinj/tools/BuildCheckpoints.java | 25 --------- 2 files changed, 5 insertions(+), 76 deletions(-) diff --git a/core/src/main/java/org/bitcoinj/core/CheckpointManager.java b/core/src/main/java/org/bitcoinj/core/CheckpointManager.java index 26d0810228e..eff70bed1f1 100644 --- a/core/src/main/java/org/bitcoinj/core/CheckpointManager.java +++ b/core/src/main/java/org/bitcoinj/core/CheckpointManager.java @@ -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.

* - *

The file format consists of the string "CHECKPOINTS 1", followed by a uint32 containing the number of signatures + *

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.

* - *

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).

+ *

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.

*/ 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; @@ -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."); @@ -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 = diff --git a/tools/src/main/java/org/bitcoinj/tools/BuildCheckpoints.java b/tools/src/main/java/org/bitcoinj/tools/BuildCheckpoints.java index 709308040a2..963e5cdc107 100644 --- a/tools/src/main/java/org/bitcoinj/tools/BuildCheckpoints.java +++ b/tools/src/main/java/org/bitcoinj/tools/BuildCheckpoints.java @@ -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 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 checkpoints, File file) throws IOException { try (PrintWriter writer = new PrintWriter(