3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-01-31 23:32:16 +00:00

Refactor compact serialization out of SPVBlockStore and into StoredBlock.

This commit is contained in:
Mike Hearn 2013-03-04 17:18:39 +01:00 committed by Mike Hearn
parent a61cd9eb19
commit 68907880cb
2 changed files with 40 additions and 30 deletions

View File

@ -19,8 +19,11 @@ package com.google.bitcoin.core;
import com.google.bitcoin.store.BlockStore; import com.google.bitcoin.store.BlockStore;
import com.google.bitcoin.store.BlockStoreException; import com.google.bitcoin.store.BlockStoreException;
import java.io.Serializable; import java.io.*;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.ByteBuffer;
import static com.google.common.base.Preconditions.checkState;
/** /**
* Wraps a {@link Block} object with extra data that can be derived from the block chain but is slow or inconvenient to * Wraps a {@link Block} object with extra data that can be derived from the block chain but is slow or inconvenient to
@ -34,6 +37,12 @@ import java.math.BigInteger;
public class StoredBlock implements Serializable { public class StoredBlock implements Serializable {
private static final long serialVersionUID = -6097565241243701771L; private static final long serialVersionUID = -6097565241243701771L;
// A BigInteger representing the total amount of work done so far on this chain. As of May 2011 it takes 8
// bytes to represent this field, so 12 bytes should be plenty for now.
public static final int CHAIN_WORK_BYTES = 12;
public static final byte[] EMPTY_BYTES = new byte[CHAIN_WORK_BYTES];
public static final int COMPACT_SERIALIZED_SIZE = Block.HEADER_SIZE + CHAIN_WORK_BYTES + 4; // for height
private Block header; private Block header;
private BigInteger chainWork; private BigInteger chainWork;
private int height; private int height;
@ -44,7 +53,6 @@ public class StoredBlock implements Serializable {
this.height = height; this.height = height;
} }
/** /**
* The block header this object wraps. The referenced block object must not have any transactions in it. * The block header this object wraps. The referenced block object must not have any transactions in it.
*/ */
@ -108,6 +116,33 @@ public class StoredBlock implements Serializable {
return store.get(getHeader().getPrevBlockHash()); return store.get(getHeader().getPrevBlockHash());
} }
/** Serializes the stored block to a custom packed format. Used by {@link CheckpointManager}. */
public void serializeCompact(ByteBuffer buffer) {
byte[] chainWorkBytes = getChainWork().toByteArray();
checkState(chainWorkBytes.length <= CHAIN_WORK_BYTES, "Ran out of space to store chain work!");
if (chainWorkBytes.length < CHAIN_WORK_BYTES) {
// Pad to the right size.
buffer.put(EMPTY_BYTES, 0, CHAIN_WORK_BYTES - chainWorkBytes.length);
}
buffer.put(chainWorkBytes);
buffer.putInt(getHeight());
// Using unsafeBitcoinSerialize here can give us direct access to the same bytes we read off the wire,
// avoiding serialization round-trips.
byte[] bytes = getHeader().unsafeBitcoinSerialize();
buffer.put(bytes, 0, Block.HEADER_SIZE); // Trim the trailing 00 byte (zero transactions).
}
/** De-serializes the stored block from a custom packed format. Used by {@link CheckpointManager}. */
public static StoredBlock deserializeCompact(NetworkParameters params, ByteBuffer buffer) throws ProtocolException {
byte[] chainWorkBytes = new byte[StoredBlock.CHAIN_WORK_BYTES];
buffer.get(chainWorkBytes);
BigInteger chainWork = new BigInteger(1, chainWorkBytes);
int height = buffer.getInt(); // +4 bytes
byte[] header = new byte[Block.HEADER_SIZE + 1]; // Extra byte for the 00 transactions length.
buffer.get(header, 0, Block.HEADER_SIZE);
return new StoredBlock(new Block(params, header), chainWork, height);
}
@Override @Override
public String toString() { public String toString() {
return String.format("Block %s at height %d: %s", return String.format("Block %s at height %d: %s",

View File

@ -173,18 +173,7 @@ public class SPVBlockStore implements BlockStore {
Sha256Hash hash = block.getHeader().getHash(); Sha256Hash hash = block.getHeader().getHash();
notFoundCache.remove(hash); notFoundCache.remove(hash);
buffer.put(hash.getBytes()); buffer.put(hash.getBytes());
byte[] chainWorkBytes = block.getChainWork().toByteArray(); block.serializeCompact(buffer);
checkState(chainWorkBytes.length <= CHAIN_WORK_BYTES, "Ran out of space to store chain work!");
if (chainWorkBytes.length < CHAIN_WORK_BYTES) {
// Pad to the right size.
buffer.put(EMPTY_BYTES, 0, CHAIN_WORK_BYTES - chainWorkBytes.length);
}
buffer.put(chainWorkBytes);
buffer.putInt(block.getHeight());
// Using unsafeBitcoinSerialize here can give us direct access to the same bytes we read off the wire,
// avoiding serialization round-trips.
byte[] bytes = block.getHeader().unsafeBitcoinSerialize();
buffer.put(bytes, 0, Block.HEADER_SIZE); // Trim the trailing 00 byte (zero transactions).
setRingCursor(buffer, buffer.position()); setRingCursor(buffer, buffer.position());
blockCache.put(hash, block); blockCache.put(hash, block);
} finally { lock.unlock(); } } finally { lock.unlock(); }
@ -220,13 +209,7 @@ public class SPVBlockStore implements BlockStore {
buffer.get(scratch); buffer.get(scratch);
if (Arrays.equals(scratch, targetHashBytes)) { if (Arrays.equals(scratch, targetHashBytes)) {
// Found the target. // Found the target.
byte[] chainWorkBytes = new byte[CHAIN_WORK_BYTES]; StoredBlock storedBlock = StoredBlock.deserializeCompact(params, buffer);
buffer.get(chainWorkBytes);
BigInteger chainWork = new BigInteger(1, chainWorkBytes);
int height = buffer.getInt(); // +4 bytes
byte[] header = new byte[Block.HEADER_SIZE + 1]; // Extra byte for the 00 transactions length.
buffer.get(header, 0, Block.HEADER_SIZE);
StoredBlock storedBlock = new StoredBlock(new Block(params, header), chainWork, height);
blockCache.put(hash, storedBlock); blockCache.put(hash, storedBlock);
return storedBlock; return storedBlock;
} }
@ -281,15 +264,7 @@ public class SPVBlockStore implements BlockStore {
} }
} }
// A BigInteger representing the total amount of work done so far on this chain. As of May 2011 it takes 8 protected static final int RECORD_SIZE = 32 /* hash */ + StoredBlock.COMPACT_SERIALIZED_SIZE;
// bytes to represent this field, so 12 bytes should be plenty for now.
protected static final int CHAIN_WORK_BYTES = 12;
protected static final byte[] EMPTY_BYTES = new byte[CHAIN_WORK_BYTES];
protected static final int RECORD_SIZE = Block.HEADER_SIZE +
32 + // for a SHA256 hash
4 + // for a height
CHAIN_WORK_BYTES; // == 128 all together.
// File format: // File format:
// 4 header bytes = "SPVB" // 4 header bytes = "SPVB"