Make BlockStore and StoredBlock public. Move StoredBlock building into the class itself.

This commit is contained in:
Mike Hearn
2011-03-27 21:31:55 +00:00
parent 990f367ef4
commit 88212f6bfa
7 changed files with 59 additions and 43 deletions

View File

@@ -72,7 +72,7 @@ public class BlockChain {
try {
blockStore = new MemoryBlockStore(params);
chainHead = blockStore.getChainHead();
LOG("chain head is: " + chainHead.header.toString());
LOG("chain head is: " + chainHead.getHeader().toString());
} catch (BlockStoreException e) {
throw new RuntimeException(e);
}
@@ -137,13 +137,13 @@ public class BlockChain {
} else {
// The block connects to somewhere on the chain. Not necessarily the top of the best known chain.
checkDifficultyTransitions(storedPrev, block);
StoredBlock newStoredBlock = buildStoredBlock(storedPrev, block);
StoredBlock newStoredBlock = storedPrev.build(block);
// Store it.
blockStore.put(newStoredBlock);
if (storedPrev.equals(chainHead)) {
// This block connects to the best known block, it is a normal continuation of the system.
setChainHead(newStoredBlock);
LOG("Received new block, chain is now " + chainHead.height + " blocks high");
LOG("Received new block, chain is now " + chainHead.getHeight() + " blocks high");
} else {
// This block connects to somewhere other than the top of the chain.
if (newStoredBlock.moreWorkThan(chainHead)) {
@@ -172,17 +172,6 @@ public class BlockChain {
}
}
/**
* Calculates the additional fields a StoredBlock holds given the previous block in the chain and the new block.
*/
private StoredBlock buildStoredBlock(StoredBlock storedPrev, Block block) throws VerificationException {
// Stored blocks track total work done in this chain, because the canonical chain is the one that represents
// the largest amount of work done not the tallest.
BigInteger chainWork = storedPrev.chainWork.add(block.getWork());
int height = storedPrev.height + 1;
return new StoredBlock(block, chainWork, height);
}
/**
* For each block in unconnectedBlocks, see if we can now fit it on top of the chain and if so, do so.
*/
@@ -219,12 +208,12 @@ public class BlockChain {
*/
private void checkDifficultyTransitions(StoredBlock storedPrev, Block next)
throws BlockStoreException, VerificationException {
Block prev = storedPrev.header;
Block prev = storedPrev.getHeader();
// Is this supposed to be a difficulty transition point?
if ((storedPrev.height + 1) % params.interval != 0) {
if ((storedPrev.getHeight() + 1) % params.interval != 0) {
// No ... so check the difficulty didn't actually change.
if (next.getDifficultyTarget() != prev.getDifficultyTarget())
throw new VerificationException("Unexpected change in difficulty at height " + storedPrev.height +
throw new VerificationException("Unexpected change in difficulty at height " + storedPrev.getHeight() +
": " + Long.toHexString(next.getDifficultyTarget()) + " vs " +
Long.toHexString(prev.getDifficultyTarget()));
return;
@@ -239,10 +228,10 @@ public class BlockChain {
throw new VerificationException(
"Difficulty transition point but we did not find a way back to the genesis block.");
}
cursor = blockStore.get(cursor.header.getPrevBlockHash());
cursor = blockStore.get(cursor.getHeader().getPrevBlockHash());
}
Block blockIntervalAgo = cursor.header;
Block blockIntervalAgo = cursor.getHeader();
int timespan = (int) (prev.getTime() - blockIntervalAgo.getTime());
// Limit the adjustment step.
if (timespan < params.targetTimespan / 4)

View File

@@ -26,7 +26,7 @@ package com.google.bitcoin.core;
*
* BlockStores are thread safe.
*/
interface BlockStore {
public interface BlockStore {
/**
* Saves the given block header+extra data. The key isn't specified explicitly as it can be calculated from the
* StoredBlock directly. Can throw if there is a problem with the underlying storage layer such as running out of

View File

@@ -20,6 +20,10 @@ package com.google.bitcoin.core;
* Thrown when something goes wrong with storing a block. Examples: out of disk space.
*/
public class BlockStoreException extends Exception {
public BlockStoreException(String message) {
super(message);
}
public BlockStoreException(Throwable t) {
super(t);
}

View File

@@ -50,7 +50,7 @@ class MemoryBlockStore implements BlockStore {
}
public synchronized void put(StoredBlock block) throws BlockStoreException {
byte[] hash = block.header.getHash();
byte[] hash = block.getHeader().getHash();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(bos);

View File

@@ -304,7 +304,7 @@ public class Peer {
// 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());
Block topBlock = blockChain.getChainHead().header;
Block topBlock = blockChain.getChainHead().getHeader();
if (!topBlock.equals(params.genesisBlock))
blockLocator.add(0, topBlock.getHash());
GetBlocksMessage message = new GetBlocksMessage(params, blockLocator, toHash);

View File

@@ -28,35 +28,46 @@ import java.math.BigInteger;
*
* StoredBlocks are put inside a {@link BlockStore} which saves them to memory or disk.
*/
class StoredBlock implements Serializable {
public class StoredBlock implements Serializable {
private static final long serialVersionUID = -6097565241243701771L;
/**
* The block header this object wraps. The referenced block object must not have any transactions in it.
*/
Block header;
private Block header;
private BigInteger chainWork;
private int height;
/**
* The total sum of work done in this block, and all the blocks below it in the chain. Work is a measure of how
* many tries are needed to solve a block. If the target is set to cover 10% of the total hash value space,
* then the work represented by a block is 10.
*/
BigInteger chainWork;
/**
* Position in the chain for this block. The genesis block has a height of zero.
*/
int height;
StoredBlock(Block header, BigInteger chainWork, int height) {
public StoredBlock(Block header, BigInteger chainWork, int height) {
assert header.transactions == null : "Should not have transactions in a block header object";
this.header = header;
this.chainWork = chainWork;
this.height = height;
}
/**
* The block header this object wraps. The referenced block object must not have any transactions in it.
*/
public Block getHeader() {
return header;
}
/**
* The total sum of work done in this block, and all the blocks below it in the chain. Work is a measure of how
* many tries are needed to solve a block. If the target is set to cover 10% of the total hash value space,
* then the work represented by a block is 10.
*/
public BigInteger getChainWork() {
return chainWork;
}
/**
* Position in the chain for this block. The genesis block has a height of zero.
*/
public int getHeight() {
return height;
}
/** Returns true if this objects chainWork is higher than the others. */
boolean moreWorkThan(StoredBlock other) {
public boolean moreWorkThan(StoredBlock other) {
return chainWork.compareTo(other.chainWork) > 0;
}
@@ -66,4 +77,16 @@ class StoredBlock implements Serializable {
StoredBlock o = (StoredBlock) other;
return o.header.equals(header) && o.chainWork.equals(chainWork) && o.height == height;
}
/**
* Creates a new StoredBlock, calculating the additional fields by adding to the values in this block.
*/
public StoredBlock build(Block block) throws VerificationException {
// Stored blocks track total work done in this chain, because the canonical chain is the one that represents
// the largest amount of work done not the tallest.
BigInteger chainWork = this.chainWork.add(block.getWork());
int height = this.height + 1;
return new StoredBlock(block, chainWork, height);
}
}

View File

@@ -383,8 +383,8 @@ public class Wallet implements Serializable {
// This runs on any peer thread with the block chain synchronized. Thus we do not have to worry about it
// being called simultaneously or repeatedly.
LOG("Re-organize!");
LOG("Old chain head: " + chainHead.header.toString());
LOG("New chain head: " + newStoredBlock.header.toString());
LOG("Old chain head: " + chainHead.getHeader().toString());
LOG("New chain head: " + newStoredBlock.getHeader().toString());
// TODO: Implement me!
// For each transaction we have to track which blocks they appeared in. Once a re-org takes place,