mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-07-29 19:11:23 +00:00
Use Sha256Hash more consistently, improve the class a bit.
Note that the endianness of the hashes is still very ad-hoc and messy. Next step is to pick an endianness and stick with it, to reduce the number of times reverseBytes is used.
This commit is contained in:
@@ -50,8 +50,8 @@ public class Block extends Message {
|
||||
// For unit testing. If not zero, use this instead of the current time.
|
||||
static long fakeClock = 0;
|
||||
private long version;
|
||||
private byte[] prevBlockHash;
|
||||
private byte[] merkleRoot;
|
||||
private Sha256Hash prevBlockHash;
|
||||
private Sha256Hash merkleRoot;
|
||||
private long time;
|
||||
private long difficultyTarget; // "nBits"
|
||||
|
||||
@@ -60,7 +60,7 @@ public class Block extends Message {
|
||||
/** If null, it means this object holds only the headers. */
|
||||
List<Transaction> transactions;
|
||||
/** Stores the hash of the block. If null, getHash() will recalculate it. */
|
||||
private transient byte[] hash;
|
||||
private transient Sha256Hash hash;
|
||||
|
||||
/** Special case constructor, used for the genesis node and unit tests. */
|
||||
Block(NetworkParameters params) {
|
||||
@@ -69,7 +69,7 @@ public class Block extends Message {
|
||||
version = 1;
|
||||
difficultyTarget = 0x1d07fff8L;
|
||||
time = System.currentTimeMillis() / 1000;
|
||||
prevBlockHash = new byte[32]; // All zeros.
|
||||
prevBlockHash = Sha256Hash.ZERO_HASH;
|
||||
}
|
||||
|
||||
/** Constructs a block object from the BitCoin wire format. */
|
||||
@@ -85,7 +85,7 @@ public class Block extends Message {
|
||||
difficultyTarget = readUint32();
|
||||
nonce = readUint32();
|
||||
|
||||
hash = Utils.reverseBytes(Utils.doubleDigest(bytes, 0, cursor));
|
||||
hash = new Sha256Hash(Utils.reverseBytes(Utils.doubleDigest(bytes, 0, cursor)));
|
||||
|
||||
if (cursor == bytes.length) {
|
||||
// This message is just a header, it has no transactions.
|
||||
@@ -103,8 +103,8 @@ public class Block extends Message {
|
||||
|
||||
private void writeHeader(OutputStream stream) throws IOException {
|
||||
Utils.uint32ToByteStreamLE(version, stream);
|
||||
stream.write(Utils.reverseBytes(prevBlockHash));
|
||||
stream.write(Utils.reverseBytes(getMerkleRoot()));
|
||||
stream.write(Utils.reverseBytes(prevBlockHash.getBytes()));
|
||||
stream.write(Utils.reverseBytes(getMerkleRoot().getBytes()));
|
||||
Utils.uint32ToByteStreamLE(time, stream);
|
||||
Utils.uint32ToByteStreamLE(difficultyTarget, stream);
|
||||
Utils.uint32ToByteStreamLE(nonce, stream);
|
||||
@@ -124,11 +124,11 @@ public class Block extends Message {
|
||||
/**
|
||||
* Calculates the block hash by serializing the block and hashing the resulting bytes.
|
||||
*/
|
||||
private byte[] calculateHash() {
|
||||
private Sha256Hash calculateHash() {
|
||||
try {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
writeHeader(bos);
|
||||
return Utils.reverseBytes(doubleDigest(bos.toByteArray()));
|
||||
return new Sha256Hash(Utils.reverseBytes(doubleDigest(bos.toByteArray())));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e); // Cannot happen.
|
||||
}
|
||||
@@ -140,13 +140,13 @@ public class Block extends Message {
|
||||
* "00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048".
|
||||
*/
|
||||
public String getHashAsString() {
|
||||
return Utils.bytesToHexString(getHash());
|
||||
return getHash().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hash of the block (which for a valid, solved block should be below the target). Big endian.
|
||||
*/
|
||||
public byte[] getHash() {
|
||||
public Sha256Hash getHash() {
|
||||
if (hash == null)
|
||||
hash = calculateHash();
|
||||
return hash;
|
||||
@@ -186,8 +186,8 @@ public class Block extends Message {
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuffer s = new StringBuffer("v" + version + " block: \n" +
|
||||
" previous block: " + bytesToHexString(prevBlockHash) + "\n" +
|
||||
" merkle root: " + bytesToHexString(getMerkleRoot()) + "\n" +
|
||||
" previous block: " + prevBlockHash.toString() + "\n" +
|
||||
" merkle root: " + getMerkleRoot().toString() + "\n" +
|
||||
" time: [" + time + "] " + new Date(time * 1000).toString() + "\n" +
|
||||
" difficulty target (nBits): " + difficultyTarget + "\n" +
|
||||
" nonce: " + nonce + "\n");
|
||||
@@ -244,7 +244,7 @@ public class Block extends Message {
|
||||
// field is of the right value. This requires us to have the preceeding blocks.
|
||||
BigInteger target = getDifficultyTargetAsInteger();
|
||||
|
||||
BigInteger h = new BigInteger(1, getHash());
|
||||
BigInteger h = getHash().toBigInteger();
|
||||
if (h.compareTo(target) > 0) {
|
||||
// Proof of work check failed!
|
||||
if (throwException)
|
||||
@@ -263,21 +263,18 @@ public class Block extends Message {
|
||||
throw new VerificationException("Block too far in future");
|
||||
}
|
||||
|
||||
private void checkMerkleHash() throws VerificationException {
|
||||
List<byte[]> tree = buildMerkleTree();
|
||||
byte[] calculatedRoot = tree.get(tree.size() - 1);
|
||||
if (!Arrays.equals(calculatedRoot, merkleRoot)) {
|
||||
log.error("Merkle tree did not verify: ");
|
||||
for (byte[] b : tree) log.error(Utils.bytesToHexString(b));
|
||||
|
||||
private void checkMerkleRoot() throws VerificationException {
|
||||
Sha256Hash calculatedRoot = calculateMerkleRoot();
|
||||
if (!calculatedRoot.equals(merkleRoot)) {
|
||||
log.error("Merkle tree did not verify");
|
||||
throw new VerificationException("Merkle hashes do not match: " +
|
||||
bytesToHexString(calculatedRoot) + " vs " + bytesToHexString(merkleRoot));
|
||||
calculatedRoot + " vs " + merkleRoot);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] calculateMerkleRoot() {
|
||||
private Sha256Hash calculateMerkleRoot() {
|
||||
List<byte[]> tree = buildMerkleTree();
|
||||
return tree.get(tree.size() - 1);
|
||||
return new Sha256Hash(tree.get(tree.size() - 1));
|
||||
}
|
||||
|
||||
private List<byte[]> buildMerkleTree() {
|
||||
@@ -315,7 +312,7 @@ public class Block extends Message {
|
||||
ArrayList<byte[]> tree = new ArrayList<byte[]>();
|
||||
// Start by adding all the hashes of the transactions as leaves of the tree.
|
||||
for (Transaction t : transactions) {
|
||||
tree.add(t.getHash().hash);
|
||||
tree.add(t.getHash().getBytes());
|
||||
}
|
||||
int levelOffset = 0; // Offset in the list where the currently processed level starts.
|
||||
// Step through each level, stopping when we reach the root (levelSize == 1).
|
||||
@@ -369,7 +366,7 @@ public class Block extends Message {
|
||||
if (transactions != null) {
|
||||
assert transactions.size() > 0;
|
||||
checkTransactions();
|
||||
checkMerkleHash();
|
||||
checkMerkleRoot();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,23 +374,23 @@ public class Block extends Message {
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof Block)) return false;
|
||||
Block other = (Block) o;
|
||||
return Arrays.equals(getHash(), other.getHash());
|
||||
return getHash().equals(other.getHash());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(getHash());
|
||||
return getHash().hashCode();
|
||||
}
|
||||
|
||||
/** Returns the merkle root in big endian form, calculating it from transactions if necessary. */
|
||||
public byte[] getMerkleRoot() {
|
||||
public Sha256Hash getMerkleRoot() {
|
||||
if (merkleRoot == null)
|
||||
merkleRoot = calculateMerkleRoot();
|
||||
return merkleRoot;
|
||||
}
|
||||
|
||||
/** Exists only for unit testing. */
|
||||
void setMerkleRoot(byte[] value) {
|
||||
void setMerkleRoot(Sha256Hash value) {
|
||||
merkleRoot = value;
|
||||
hash = null;
|
||||
}
|
||||
@@ -415,11 +412,11 @@ public class Block extends Message {
|
||||
}
|
||||
|
||||
/** Returns the hash of the previous block in the chain, as defined by the block header. */
|
||||
public byte[] getPrevBlockHash() {
|
||||
public Sha256Hash getPrevBlockHash() {
|
||||
return prevBlockHash;
|
||||
}
|
||||
|
||||
void setPrevBlockHash(byte[] prevBlockHash) {
|
||||
void setPrevBlockHash(Sha256Hash prevBlockHash) {
|
||||
this.prevBlockHash = prevBlockHash;
|
||||
this.hash = null;
|
||||
}
|
||||
|
@@ -22,10 +22,10 @@ import java.util.List;
|
||||
|
||||
public class GetBlocksMessage extends Message {
|
||||
private static final long serialVersionUID = 3479412877853645644L;
|
||||
private final List<byte[]> locator;
|
||||
private final byte[] stopHash;
|
||||
private final List<Sha256Hash> locator;
|
||||
private final Sha256Hash stopHash;
|
||||
|
||||
public GetBlocksMessage(NetworkParameters params, List<byte[]> locator, byte[] stopHash) {
|
||||
public GetBlocksMessage(NetworkParameters params, List<Sha256Hash> locator, Sha256Hash stopHash) {
|
||||
super(params);
|
||||
this.locator = locator;
|
||||
this.stopHash = stopHash;
|
||||
@@ -37,8 +37,8 @@ public class GetBlocksMessage extends Message {
|
||||
public String toString() {
|
||||
StringBuffer b = new StringBuffer();
|
||||
b.append("getblocks: ");
|
||||
for (byte[] hash : locator) {
|
||||
b.append(Utils.bytesToHexString(hash));
|
||||
for (Sha256Hash hash : locator) {
|
||||
b.append(hash.toString());
|
||||
b.append(" ");
|
||||
}
|
||||
return b.toString();
|
||||
@@ -53,12 +53,12 @@ public class GetBlocksMessage extends Message {
|
||||
// identifiers that spans the entire chain with exponentially increasing gaps between
|
||||
// them, until we end up at the genesis block. See CBlockLocator::Set()
|
||||
buf.write(new VarInt(locator.size()).encode());
|
||||
for (byte[] hash : locator) {
|
||||
for (Sha256Hash hash : locator) {
|
||||
// Have to reverse as wire format is little endian.
|
||||
buf.write(Utils.reverseBytes(hash));
|
||||
buf.write(Utils.reverseBytes(hash.getBytes()));
|
||||
}
|
||||
// Next, a block ID to stop at.
|
||||
buf.write(stopHash);
|
||||
buf.write(stopHash.getBytes());
|
||||
return buf.toByteArray();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e); // Cannot happen.
|
||||
|
@@ -24,15 +24,15 @@ public class InventoryItem {
|
||||
}
|
||||
|
||||
public final Type type;
|
||||
public final byte[] hash;
|
||||
public final Sha256Hash hash;
|
||||
|
||||
public InventoryItem(Type type, byte[] hash) {
|
||||
public InventoryItem(Type type, Sha256Hash hash) {
|
||||
this.type = type;
|
||||
this.hash = hash;
|
||||
}
|
||||
|
||||
|
||||
public String toString() {
|
||||
return type.toString() + ": " + Utils.bytesToHexString(hash);
|
||||
return type.toString() + ": " + hash;
|
||||
}
|
||||
}
|
||||
|
@@ -81,14 +81,14 @@ public abstract class ListMessage extends Message
|
||||
|
||||
|
||||
@Override
|
||||
public void bitcoinSerializeToStream( OutputStream stream) throws IOException
|
||||
public void bitcoinSerializeToStream(OutputStream stream) throws IOException
|
||||
{
|
||||
stream.write(new VarInt(items.size()).encode());
|
||||
for (InventoryItem i : items) {
|
||||
// Write out the type code.
|
||||
Utils.uint32ToByteStreamLE(i.type.ordinal(), stream);
|
||||
// And now the hash.
|
||||
stream.write(Utils.reverseBytes(i.hash));
|
||||
stream.write(Utils.reverseBytes(i.hash.getBytes()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -113,14 +113,14 @@ public abstract class Message implements Serializable {
|
||||
return u;
|
||||
}
|
||||
|
||||
byte[] readHash() {
|
||||
Sha256Hash readHash() {
|
||||
byte[] hash = new byte[32];
|
||||
System.arraycopy(bytes, cursor, hash, 0, 32);
|
||||
// We have to flip it around, as it's been read off the wire in little endian.
|
||||
// Not the most efficient way to do this but the clearest.
|
||||
hash = Utils.reverseBytes(hash);
|
||||
cursor += 32;
|
||||
return hash;
|
||||
return new Sha256Hash(hash);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -115,7 +115,7 @@ public class Peer {
|
||||
synchronized (pendingGetBlockFutures) {
|
||||
for (int i = 0; i < pendingGetBlockFutures.size(); i++) {
|
||||
GetDataFuture<Block> f = pendingGetBlockFutures.get(i);
|
||||
if (Arrays.equals(f.getItem().hash, m.getHash())) {
|
||||
if (f.getItem().hash.equals(m.getHash())) {
|
||||
// Yes, it was. So pass it through the future.
|
||||
f.setResult(m);
|
||||
// Blocks explicitly requested don't get sent to the block chain.
|
||||
@@ -161,10 +161,10 @@ public class Peer {
|
||||
// chain, we may end up requesting blocks we already requested before. This shouldn't (in theory) happen
|
||||
// enough to be a problem.
|
||||
Block topBlock = blockChain.getUnconnectedBlock();
|
||||
byte[] topHash = (topBlock != null ? topBlock.getHash() : null);
|
||||
Sha256Hash topHash = (topBlock != null ? topBlock.getHash() : null);
|
||||
List<InventoryItem> items = inv.getItems();
|
||||
if (items.size() == 1 && items.get(0).type == InventoryItem.Type.Block && topHash != null &&
|
||||
Arrays.equals(items.get(0).hash, topHash)) {
|
||||
items.get(0).hash.equals(topHash)) {
|
||||
// An inv with a single hash containing our most recent unconnected block is a special inv,
|
||||
// it's kind of like a tickle from the peer telling us that it's time to download more blocks to catch up to
|
||||
// the block chain. We could just ignore this and treat it as a regular inv but then we'd download the head
|
||||
@@ -196,7 +196,7 @@ public class Peer {
|
||||
* @param blockHash Hash of the block you wareare requesting.
|
||||
* @throws IOException
|
||||
*/
|
||||
public Future<Block> getBlock(byte[] blockHash) throws IOException {
|
||||
public Future<Block> getBlock(Sha256Hash blockHash) throws IOException {
|
||||
InventoryMessage getdata = new InventoryMessage(params);
|
||||
InventoryItem inventoryItem = new InventoryItem(InventoryItem.Type.Block, blockHash);
|
||||
getdata.addItem(inventoryItem);
|
||||
@@ -273,7 +273,7 @@ public class Peer {
|
||||
conn.writeMessage(tx);
|
||||
}
|
||||
|
||||
private void blockChainDownload(byte[] toHash) throws IOException {
|
||||
private void blockChainDownload(Sha256Hash toHash) throws IOException {
|
||||
// This may run in ANY thread.
|
||||
|
||||
// The block chain download process is a bit complicated. Basically, we start with zero or more blocks in a
|
||||
@@ -301,10 +301,10 @@ public class Peer {
|
||||
//
|
||||
// So this is a complicated process but it has the advantage that we can download a chain of enormous length
|
||||
// in a relatively stateless manner and with constant/bounded memory usage.
|
||||
log.info("blockChainDownload({})", Utils.bytesToHexString(toHash));
|
||||
log.info("blockChainDownload({})", toHash.toString());
|
||||
|
||||
// TODO: Block locators should be abstracted out rather than special cased here.
|
||||
List<byte[]> blockLocator = new LinkedList<byte[]>();
|
||||
List<Sha256Hash> blockLocator = new LinkedList<Sha256Hash>();
|
||||
// We don't do the exponential thinning here, so if we get onto a fork of the chain we will end up
|
||||
// redownloading the whole thing again.
|
||||
blockLocator.add(params.genesisBlock.getHash());
|
||||
@@ -333,7 +333,7 @@ public class Peer {
|
||||
chainCompletionLatch = new CountDownLatch(blocksToGet);
|
||||
if (blocksToGet > 0) {
|
||||
// When we just want as many blocks as possible, we can set the target hash to zero.
|
||||
blockChainDownload(new byte[32]);
|
||||
blockChainDownload(Sha256Hash.ZERO_HASH);
|
||||
}
|
||||
return chainCompletionLatch;
|
||||
}
|
||||
|
@@ -16,42 +16,61 @@
|
||||
|
||||
package com.google.bitcoin.core;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import com.google.bitcoin.bouncycastle.util.encoders.Hex;
|
||||
|
||||
// TODO: Switch all code/interfaces to using this class.
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* A Sha256Hash just wraps a byte[] so that equals and hashcode work correctly, allowing it to be used as keys in a
|
||||
* map. It also checks that the length is correct and provides a bit more type safety.
|
||||
*/
|
||||
public class Sha256Hash implements Serializable {
|
||||
public byte[] hash;
|
||||
private byte[] bytes;
|
||||
|
||||
public Sha256Hash(byte[] hash) {
|
||||
assert hash.length == 32;
|
||||
this.hash = hash;
|
||||
public static Sha256Hash ZERO_HASH = new Sha256Hash(new byte[32]);
|
||||
|
||||
/** Creates a Sha256Hash by wrapping the given byte array. It must be 32 bytes long. */
|
||||
public Sha256Hash(byte[] bytes) {
|
||||
assert bytes.length == 32;
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
/** Creates a Sha256Hash by decoding the given hex string. It must be 64 characters long. */
|
||||
public Sha256Hash(String string) {
|
||||
assert string.length() == 64;
|
||||
this.bytes = Hex.decode(string);
|
||||
}
|
||||
|
||||
/** Returns true if the hashes are equal. */
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof Sha256Hash)) return false;
|
||||
return Arrays.equals(hash, ((Sha256Hash)other).hash);
|
||||
return Arrays.equals(bytes, ((Sha256Hash)other).bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash code of the byte array as calculated by {@link Arrays#hashCode()}. Note the difference between a SHA256
|
||||
* secure hash and the type of quick/dirty hash used by the Java hashCode method which is designed for use in
|
||||
* hash tables.
|
||||
* secure bytes and the type of quick/dirty bytes used by the Java hashCode method which is designed for use in
|
||||
* bytes tables.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(hash);
|
||||
return Arrays.hashCode(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Utils.bytesToHexString(hash);
|
||||
return Utils.bytesToHexString(bytes);
|
||||
}
|
||||
|
||||
/** Returns the bytes interpreted as a positive integer. */
|
||||
public BigInteger toBigInteger() {
|
||||
return new BigInteger(1, bytes);
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
@@ -448,6 +448,6 @@ public class Transaction extends Message implements Serializable {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(getHash().hash);
|
||||
return getHash().hashCode();
|
||||
}
|
||||
}
|
||||
|
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.google.bitcoin.core;
|
||||
|
||||
import com.sun.tools.internal.ws.wsdl.document.Output;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Serializable;
|
||||
@@ -93,9 +95,7 @@ public class TransactionInput extends Message implements Serializable {
|
||||
* Coinbase transactions have special inputs with hashes of zero. If this is such an input, returns true.
|
||||
*/
|
||||
public boolean isCoinBase() {
|
||||
for (int i = 0; i < outpoint.hash.length; i++)
|
||||
if (outpoint.hash[i] != 0) return false;
|
||||
return true;
|
||||
return outpoint.hash.equals(Sha256Hash.ZERO_HASH);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,8 +146,7 @@ public class TransactionInput extends Message implements Serializable {
|
||||
* @return The TransactionOutput or null if the transactions map doesn't contain the referenced tx.
|
||||
*/
|
||||
TransactionOutput getConnectedOutput(Map<Sha256Hash, Transaction> transactions) {
|
||||
Sha256Hash h = new Sha256Hash(outpoint.hash);
|
||||
Transaction tx = transactions.get(h);
|
||||
Transaction tx = transactions.get(outpoint.hash);
|
||||
if (tx == null)
|
||||
return null;
|
||||
TransactionOutput out = tx.outputs.get((int)outpoint.index);
|
||||
@@ -163,8 +162,7 @@ public class TransactionInput extends Message implements Serializable {
|
||||
* @return true if connection took place, false if the referenced transaction was not in the list.
|
||||
*/
|
||||
ConnectionResult connect(Map<Sha256Hash, Transaction> transactions, boolean disconnect) {
|
||||
Sha256Hash h = new Sha256Hash(outpoint.hash);
|
||||
Transaction tx = transactions.get(h);
|
||||
Transaction tx = transactions.get(outpoint.hash);
|
||||
if (tx == null)
|
||||
return TransactionInput.ConnectionResult.NO_SUCH_TX;
|
||||
TransactionOutput out = tx.outputs.get((int)outpoint.index);
|
||||
|
@@ -29,7 +29,7 @@ public class TransactionOutPoint extends Message implements Serializable {
|
||||
private static final long serialVersionUID = -6320880638344662579L;
|
||||
|
||||
/** Hash of the transaction to which we refer. */
|
||||
byte[] hash;
|
||||
Sha256Hash hash;
|
||||
/** Which output of that transaction we are talking about. */
|
||||
long index;
|
||||
|
||||
@@ -41,11 +41,11 @@ public class TransactionOutPoint extends Message implements Serializable {
|
||||
super(params);
|
||||
this.index = index;
|
||||
if (fromTx != null) {
|
||||
this.hash = fromTx.getHash().hash;
|
||||
this.hash = fromTx.getHash();
|
||||
this.fromTx = fromTx;
|
||||
} else {
|
||||
// This happens when constructing the genesis block.
|
||||
hash = new byte[32]; // All zeros.
|
||||
hash = Sha256Hash.ZERO_HASH;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,8 +62,7 @@ public class TransactionOutPoint extends Message implements Serializable {
|
||||
|
||||
@Override
|
||||
public void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
assert hash.length == 32;
|
||||
stream.write(Utils.reverseBytes(hash));
|
||||
stream.write(Utils.reverseBytes(hash.getBytes()));
|
||||
Utils.uint32ToByteStreamLE(index, stream);
|
||||
}
|
||||
|
||||
|
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.google.bitcoin.store;
|
||||
|
||||
import com.google.bitcoin.core.Sha256Hash;
|
||||
import com.google.bitcoin.core.StoredBlock;
|
||||
|
||||
/**
|
||||
@@ -40,7 +41,7 @@ public interface BlockStore {
|
||||
* Returns the StoredBlock given a hash. The returned values block.getHash() method will be equal to the
|
||||
* parameter. If no such block is found, returns null.
|
||||
*/
|
||||
StoredBlock get(byte[] hash) throws BlockStoreException;
|
||||
StoredBlock get(Sha256Hash hash) throws BlockStoreException;
|
||||
|
||||
/**
|
||||
* Returns the {@link StoredBlock} that represents the top of the chain of greatest total work.
|
||||
|
@@ -166,8 +166,8 @@ public class BoundedOverheadBlockStore implements BlockStore {
|
||||
// Set up the genesis block. When we start out fresh, it is by definition the top of the chain.
|
||||
Block genesis = params.genesisBlock.cloneAsHeader();
|
||||
StoredBlock storedGenesis = new StoredBlock(genesis, genesis.getWork(), 0);
|
||||
this.chainHead = new Sha256Hash(storedGenesis.getHeader().getHash());
|
||||
this.file.write(this.chainHead.hash);
|
||||
this.chainHead = storedGenesis.getHeader().getHash();
|
||||
this.file.write(this.chainHead.getBytes());
|
||||
put(storedGenesis);
|
||||
} catch (VerificationException e1) {
|
||||
throw new RuntimeException(e1); // Cannot happen.
|
||||
@@ -202,7 +202,7 @@ public class BoundedOverheadBlockStore implements BlockStore {
|
||||
|
||||
public synchronized void put(StoredBlock block) throws BlockStoreException {
|
||||
try {
|
||||
Sha256Hash hash = new Sha256Hash(block.getHeader().getHash());
|
||||
Sha256Hash hash = block.getHeader().getHash();
|
||||
// Append to the end of the file.
|
||||
dummyRecord.write(channel, block);
|
||||
blockCache.put(hash, block);
|
||||
@@ -211,9 +211,8 @@ public class BoundedOverheadBlockStore implements BlockStore {
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized StoredBlock get(byte[] hashBytes) throws BlockStoreException {
|
||||
public synchronized StoredBlock get(Sha256Hash hash) throws BlockStoreException {
|
||||
// Check the memory cache first.
|
||||
Sha256Hash hash = new Sha256Hash(hashBytes);
|
||||
StoredBlock fromMem = blockCache.get(hash);
|
||||
if (fromMem != null) {
|
||||
return fromMem;
|
||||
@@ -248,7 +247,7 @@ public class BoundedOverheadBlockStore implements BlockStore {
|
||||
do {
|
||||
if (!record.read(channel, pos, buf))
|
||||
throw new IOException("Failed to read buffer");
|
||||
if (Arrays.equals(record.getHeader(params).getHash(), hash.hash)) {
|
||||
if (record.getHeader(params).getHash().equals(hash)) {
|
||||
// Found it. Update file position for next time.
|
||||
channel.position(pos);
|
||||
return record;
|
||||
@@ -269,15 +268,14 @@ public class BoundedOverheadBlockStore implements BlockStore {
|
||||
}
|
||||
|
||||
public synchronized StoredBlock getChainHead() throws BlockStoreException {
|
||||
return get(chainHead.hash);
|
||||
return get(chainHead);
|
||||
}
|
||||
|
||||
public synchronized void setChainHead(StoredBlock chainHead) throws BlockStoreException {
|
||||
try {
|
||||
byte[] hash = chainHead.getHeader().getHash();
|
||||
this.chainHead = new Sha256Hash(hash);
|
||||
this.chainHead = chainHead.getHeader().getHash();
|
||||
// Write out new hash to the first 32 bytes of the file past one (first byte is version number).
|
||||
channel.write(ByteBuffer.wrap(hash), 1);
|
||||
channel.write(ByteBuffer.wrap(this.chainHead.getBytes()), 1);
|
||||
} catch (IOException e) {
|
||||
throw new BlockStoreException(e);
|
||||
}
|
||||
|
@@ -63,8 +63,8 @@ public class DiskBlockStore implements BlockStore {
|
||||
// Set up the genesis block. When we start out fresh, it is by definition the top of the chain.
|
||||
Block genesis = params.genesisBlock.cloneAsHeader();
|
||||
StoredBlock storedGenesis = new StoredBlock(genesis, genesis.getWork(), 0);
|
||||
this.chainHead = new Sha256Hash(storedGenesis.getHeader().getHash());
|
||||
stream.write(this.chainHead.hash);
|
||||
this.chainHead = storedGenesis.getHeader().getHash();
|
||||
stream.write(this.chainHead.getBytes());
|
||||
put(storedGenesis);
|
||||
} catch (VerificationException e1) {
|
||||
throw new RuntimeException(e1); // Cannot happen.
|
||||
@@ -110,8 +110,8 @@ public class DiskBlockStore implements BlockStore {
|
||||
if (b.equals(params.genesisBlock)) {
|
||||
s = new StoredBlock(params.genesisBlock.cloneAsHeader(), params.genesisBlock.getWork(), 0);
|
||||
} else {
|
||||
throw new BlockStoreException("Could not connect " + Utils.bytesToHexString(b.getHash()) + " to "
|
||||
+ Utils.bytesToHexString(b.getPrevBlockHash()));
|
||||
throw new BlockStoreException("Could not connect " + b.getHash().toString() + " to "
|
||||
+ b.getPrevBlockHash().toString());
|
||||
}
|
||||
} else {
|
||||
// Don't try to verify the genesis block to avoid upsetting the unit tests.
|
||||
@@ -120,7 +120,7 @@ public class DiskBlockStore implements BlockStore {
|
||||
s = prev.build(b);
|
||||
}
|
||||
// Save in memory.
|
||||
blockMap.put(new Sha256Hash(b.getHash()), s);
|
||||
blockMap.put(b.getHash(), s);
|
||||
}
|
||||
} catch (ProtocolException e) {
|
||||
// Corrupted file.
|
||||
@@ -135,7 +135,7 @@ public class DiskBlockStore implements BlockStore {
|
||||
|
||||
public synchronized void put(StoredBlock block) throws BlockStoreException {
|
||||
try {
|
||||
Sha256Hash hash = new Sha256Hash(block.getHeader().getHash());
|
||||
Sha256Hash hash = block.getHeader().getHash();
|
||||
assert blockMap.get(hash) == null : "Attempt to insert duplicate";
|
||||
// Append to the end of the file. The other fields in StoredBlock will be recalculated when it's reloaded.
|
||||
byte[] bytes = block.getHeader().bitcoinSerialize();
|
||||
@@ -147,8 +147,8 @@ public class DiskBlockStore implements BlockStore {
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized StoredBlock get(byte[] hash) throws BlockStoreException {
|
||||
return blockMap.get(new Sha256Hash(hash));
|
||||
public synchronized StoredBlock get(Sha256Hash hash) throws BlockStoreException {
|
||||
return blockMap.get(hash);
|
||||
}
|
||||
|
||||
public synchronized StoredBlock getChainHead() throws BlockStoreException {
|
||||
@@ -157,10 +157,9 @@ public class DiskBlockStore implements BlockStore {
|
||||
|
||||
public synchronized void setChainHead(StoredBlock chainHead) throws BlockStoreException {
|
||||
try {
|
||||
byte[] hash = chainHead.getHeader().getHash();
|
||||
this.chainHead = new Sha256Hash(hash);
|
||||
this.chainHead = chainHead.getHeader().getHash();
|
||||
// Write out new hash to the first 32 bytes of the file past one (first byte is version number).
|
||||
stream.getChannel().write(ByteBuffer.wrap(hash), 1);
|
||||
stream.getChannel().write(ByteBuffer.wrap(this.chainHead.getBytes()), 1);
|
||||
} catch (IOException e) {
|
||||
throw new BlockStoreException(e);
|
||||
}
|
||||
|
@@ -27,17 +27,11 @@ import java.util.Map;
|
||||
* Keeps {@link com.google.bitcoin.core.StoredBlock}s in memory. Used primarily for unit testing.
|
||||
*/
|
||||
public class MemoryBlockStore implements BlockStore {
|
||||
// We use a ByteBuffer to hold hashes here because the Java array equals()/hashcode() methods do not operate on
|
||||
// the contents of the array but just inherit the default Object behavior. ByteBuffer provides the functionality
|
||||
// needed to act as a key in a map.
|
||||
//
|
||||
// The StoredBlocks are also stored as serialized objects to ensure we don't have assumptions that would make
|
||||
// things harder for disk based implementations.
|
||||
private Map<ByteBuffer, byte[]> blockMap;
|
||||
private Map<Sha256Hash, StoredBlock> blockMap;
|
||||
private StoredBlock chainHead;
|
||||
|
||||
public MemoryBlockStore(NetworkParameters params) {
|
||||
blockMap = new HashMap<ByteBuffer, byte[]>();
|
||||
blockMap = new HashMap<Sha256Hash, StoredBlock>();
|
||||
// Insert the genesis block.
|
||||
try {
|
||||
Block genesisHeader = params.genesisBlock.cloneAsHeader();
|
||||
@@ -52,31 +46,12 @@ public class MemoryBlockStore implements BlockStore {
|
||||
}
|
||||
|
||||
public synchronized void put(StoredBlock block) throws BlockStoreException {
|
||||
byte[] hash = block.getHeader().getHash();
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
try {
|
||||
ObjectOutputStream oos = new ObjectOutputStream(bos);
|
||||
oos.writeObject(block);
|
||||
oos.close();
|
||||
blockMap.put(ByteBuffer.wrap(hash), bos.toByteArray());
|
||||
} catch (IOException e) {
|
||||
throw new BlockStoreException(e);
|
||||
}
|
||||
Sha256Hash hash = block.getHeader().getHash();
|
||||
blockMap.put(hash, block);
|
||||
}
|
||||
|
||||
public synchronized StoredBlock get(byte[] hash) throws BlockStoreException {
|
||||
try {
|
||||
byte[] serializedBlock = blockMap.get(ByteBuffer.wrap(hash));
|
||||
if (serializedBlock == null)
|
||||
return null;
|
||||
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(serializedBlock));
|
||||
StoredBlock storedBlock = (StoredBlock) ois.readObject();
|
||||
return storedBlock;
|
||||
} catch (IOException e) {
|
||||
throw new BlockStoreException(e);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new BlockStoreException(e);
|
||||
}
|
||||
public synchronized StoredBlock get(Sha256Hash hash) throws BlockStoreException {
|
||||
return blockMap.get(hash);
|
||||
}
|
||||
|
||||
public StoredBlock getChainHead() {
|
||||
|
@@ -123,7 +123,7 @@ public class BlockChainTest {
|
||||
NetworkParameters params2 = NetworkParameters.testNet();
|
||||
Block bad = new Block(params2);
|
||||
// Merkle root can be anything here, doesn't matter.
|
||||
bad.setMerkleRoot(Hex.decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
|
||||
bad.setMerkleRoot(new Sha256Hash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
|
||||
// Nonce was just some number that made the hash < difficulty limit set below, it can be anything.
|
||||
bad.setNonce(140548933);
|
||||
bad.setTime(1279242649);
|
||||
@@ -158,10 +158,10 @@ public class BlockChainTest {
|
||||
// Some blocks from the test net.
|
||||
private Block getBlock2() throws Exception {
|
||||
Block b2 = new Block(testNet);
|
||||
b2.setMerkleRoot(Hex.decode("addc858a17e21e68350f968ccd384d6439b64aafa6c193c8b9dd66320470838b"));
|
||||
b2.setMerkleRoot(new Sha256Hash("addc858a17e21e68350f968ccd384d6439b64aafa6c193c8b9dd66320470838b"));
|
||||
b2.setNonce(2642058077L);
|
||||
b2.setTime(1296734343L);
|
||||
b2.setPrevBlockHash(Hex.decode("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604"));
|
||||
b2.setPrevBlockHash(new Sha256Hash("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604"));
|
||||
assertEquals("000000037b21cac5d30fc6fda2581cf7b2612908aed2abbcc429c45b0557a15f", b2.getHashAsString());
|
||||
b2.verify();
|
||||
return b2;
|
||||
@@ -169,10 +169,10 @@ public class BlockChainTest {
|
||||
|
||||
private Block getBlock1() throws Exception {
|
||||
Block b1 = new Block(testNet);
|
||||
b1.setMerkleRoot(Hex.decode("0e8e58ecdacaa7b3c6304a35ae4ffff964816d2b80b62b58558866ce4e648c10"));
|
||||
b1.setMerkleRoot(new Sha256Hash("0e8e58ecdacaa7b3c6304a35ae4ffff964816d2b80b62b58558866ce4e648c10"));
|
||||
b1.setNonce(236038445);
|
||||
b1.setTime(1296734340);
|
||||
b1.setPrevBlockHash(Hex.decode("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"));
|
||||
b1.setPrevBlockHash(new Sha256Hash("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"));
|
||||
assertEquals("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604", b1.getHashAsString());
|
||||
b1.verify();
|
||||
return b1;
|
||||
|
Reference in New Issue
Block a user