mirror of
https://github.com/bitcoinj/bitcoinj.git
synced 2025-02-24 06:47:54 +01:00
CheckpointManager, BuildCheckpoints: remove support for binary checkpoint format
Use the textual format instead.
This commit is contained in:
parent
f5c36e1a80
commit
db329beca5
2 changed files with 6 additions and 77 deletions
|
@ -66,19 +66,18 @@ import static org.bitcoinj.base.internal.Preconditions.checkState;
|
||||||
* different concept of checkpoints that are used to hard-code the validity of blocks that violate BIP30 (duplicate
|
* 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>
|
* 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>Checkpoints are read from a text file, one value per line.
|
||||||
* to read. The value may not be larger than 256 (so it could have been a byte but isn't for historical reasons).
|
* 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.
|
||||||
* If the number of signatures is larger than zero, each 65 byte ECDSA secp256k1 signature then follows. The signatures
|
* 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>
|
* 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
|
* <p>After the signatures come the number of checkpoints in the file. Then each checkpoint follows one per line in
|
||||||
* one after the other. A checkpoint is 12 bytes for the total work done field, 4 bytes for the height, 80 bytes
|
* compact format (as written by {@link StoredBlock#serializeCompact(ByteBuffer)}) as a base64-encoded blob.</p>
|
||||||
* for the block header and then 1 zero byte at the end (i.e. number of transactions in the block: always zero).</p>
|
|
||||||
*/
|
*/
|
||||||
public class CheckpointManager {
|
public class CheckpointManager {
|
||||||
private static final Logger log = LoggerFactory.getLogger(CheckpointManager.class);
|
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 String TEXTUAL_MAGIC = "TXT CHECKPOINTS 1";
|
||||||
private static final int MAX_SIGNATURES = 256;
|
private static final int MAX_SIGNATURES = 256;
|
||||||
|
|
||||||
|
@ -105,9 +104,7 @@ public class CheckpointManager {
|
||||||
inputStream.mark(1);
|
inputStream.mark(1);
|
||||||
int first = inputStream.read();
|
int first = inputStream.read();
|
||||||
inputStream.reset();
|
inputStream.reset();
|
||||||
if (first == BINARY_MAGIC.charAt(0))
|
if (first == TEXTUAL_MAGIC.charAt(0))
|
||||||
dataHash = readBinary(inputStream);
|
|
||||||
else if (first == TEXTUAL_MAGIC.charAt(0))
|
|
||||||
dataHash = readTextual(inputStream);
|
dataHash = readTextual(inputStream);
|
||||||
else
|
else
|
||||||
throw new IOException("Unsupported format.");
|
throw new IOException("Unsupported format.");
|
||||||
|
@ -118,49 +115,6 @@ public class CheckpointManager {
|
||||||
return CheckpointManager.class.getResourceAsStream("/" + params.getId() + ".checkpoints.txt");
|
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 {
|
private Sha256Hash readTextual(InputStream inputStream) throws IOException {
|
||||||
Hasher hasher = Hashing.sha256().newHasher();
|
Hasher hasher = Hashing.sha256().newHasher();
|
||||||
try (BufferedReader reader =
|
try (BufferedReader reader =
|
||||||
|
|
|
@ -158,45 +158,20 @@ public class BuildCheckpoints implements Callable<Integer> {
|
||||||
|
|
||||||
checkState(checkpoints.size() > 0);
|
checkState(checkpoints.size() > 0);
|
||||||
|
|
||||||
final File plainFile = new File("checkpoints" + suffix);
|
|
||||||
final File textFile = new File("checkpoints" + suffix + ".txt");
|
final File textFile = new File("checkpoints" + suffix + ".txt");
|
||||||
|
|
||||||
// Write checkpoint data out.
|
// Write checkpoint data out.
|
||||||
writeBinaryCheckpoints(checkpoints, plainFile);
|
|
||||||
writeTextualCheckpoints(checkpoints, textFile);
|
writeTextualCheckpoints(checkpoints, textFile);
|
||||||
|
|
||||||
peerGroup.stop();
|
peerGroup.stop();
|
||||||
store.close();
|
store.close();
|
||||||
|
|
||||||
// Sanity check the created files.
|
// Sanity check the created files.
|
||||||
sanityCheck(plainFile, checkpoints.size());
|
|
||||||
sanityCheck(textFile, checkpoints.size());
|
sanityCheck(textFile, checkpoints.size());
|
||||||
|
|
||||||
return 0;
|
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)
|
private static void writeTextualCheckpoints(TreeMap<Integer, StoredBlock> checkpoints, File file)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
try (PrintWriter writer = new PrintWriter(
|
try (PrintWriter writer = new PrintWriter(
|
||||||
|
|
Loading…
Add table
Reference in a new issue