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 { try {
blockStore = new MemoryBlockStore(params); blockStore = new MemoryBlockStore(params);
chainHead = blockStore.getChainHead(); chainHead = blockStore.getChainHead();
LOG("chain head is: " + chainHead.header.toString()); LOG("chain head is: " + chainHead.getHeader().toString());
} catch (BlockStoreException e) { } catch (BlockStoreException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -137,13 +137,13 @@ public class BlockChain {
} else { } else {
// The block connects to somewhere on the chain. Not necessarily the top of the best known chain. // The block connects to somewhere on the chain. Not necessarily the top of the best known chain.
checkDifficultyTransitions(storedPrev, block); checkDifficultyTransitions(storedPrev, block);
StoredBlock newStoredBlock = buildStoredBlock(storedPrev, block); StoredBlock newStoredBlock = storedPrev.build(block);
// Store it. // Store it.
blockStore.put(newStoredBlock); blockStore.put(newStoredBlock);
if (storedPrev.equals(chainHead)) { if (storedPrev.equals(chainHead)) {
// This block connects to the best known block, it is a normal continuation of the system. // This block connects to the best known block, it is a normal continuation of the system.
setChainHead(newStoredBlock); 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 { } else {
// This block connects to somewhere other than the top of the chain. // This block connects to somewhere other than the top of the chain.
if (newStoredBlock.moreWorkThan(chainHead)) { 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. * 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) private void checkDifficultyTransitions(StoredBlock storedPrev, Block next)
throws BlockStoreException, VerificationException { throws BlockStoreException, VerificationException {
Block prev = storedPrev.header; Block prev = storedPrev.getHeader();
// Is this supposed to be a difficulty transition point? // 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. // No ... so check the difficulty didn't actually change.
if (next.getDifficultyTarget() != prev.getDifficultyTarget()) 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(next.getDifficultyTarget()) + " vs " +
Long.toHexString(prev.getDifficultyTarget())); Long.toHexString(prev.getDifficultyTarget()));
return; return;
@@ -239,10 +228,10 @@ public class BlockChain {
throw new VerificationException( throw new VerificationException(
"Difficulty transition point but we did not find a way back to the genesis block."); "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()); int timespan = (int) (prev.getTime() - blockIntervalAgo.getTime());
// Limit the adjustment step. // Limit the adjustment step.
if (timespan < params.targetTimespan / 4) if (timespan < params.targetTimespan / 4)

View File

@@ -26,7 +26,7 @@ package com.google.bitcoin.core;
* *
* BlockStores are thread safe. * 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 * 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 * 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. * Thrown when something goes wrong with storing a block. Examples: out of disk space.
*/ */
public class BlockStoreException extends Exception { public class BlockStoreException extends Exception {
public BlockStoreException(String message) {
super(message);
}
public BlockStoreException(Throwable t) { public BlockStoreException(Throwable t) {
super(t); super(t);
} }

View File

@@ -50,7 +50,7 @@ class MemoryBlockStore implements BlockStore {
} }
public synchronized void put(StoredBlock block) throws BlockStoreException { public synchronized void put(StoredBlock block) throws BlockStoreException {
byte[] hash = block.header.getHash(); byte[] hash = block.getHeader().getHash();
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream();
try { try {
ObjectOutputStream oos = new ObjectOutputStream(bos); 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 // 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. // redownloading the whole thing again.
blockLocator.add(params.genesisBlock.getHash()); blockLocator.add(params.genesisBlock.getHash());
Block topBlock = blockChain.getChainHead().header; Block topBlock = blockChain.getChainHead().getHeader();
if (!topBlock.equals(params.genesisBlock)) if (!topBlock.equals(params.genesisBlock))
blockLocator.add(0, topBlock.getHash()); blockLocator.add(0, topBlock.getHash());
GetBlocksMessage message = new GetBlocksMessage(params, blockLocator, toHash); 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. * 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; private static final long serialVersionUID = -6097565241243701771L;
/** private Block header;
* The block header this object wraps. The referenced block object must not have any transactions in it. private BigInteger chainWork;
*/ private int height;
Block header;
/** public StoredBlock(Block header, BigInteger chainWork, 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) {
assert header.transactions == null : "Should not have transactions in a block header object"; assert header.transactions == null : "Should not have transactions in a block header object";
this.header = header; this.header = header;
this.chainWork = chainWork; this.chainWork = chainWork;
this.height = height; 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. */ /** 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; return chainWork.compareTo(other.chainWork) > 0;
} }
@@ -66,4 +77,16 @@ class StoredBlock implements Serializable {
StoredBlock o = (StoredBlock) other; StoredBlock o = (StoredBlock) other;
return o.header.equals(header) && o.chainWork.equals(chainWork) && o.height == height; 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 // 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. // being called simultaneously or repeatedly.
LOG("Re-organize!"); LOG("Re-organize!");
LOG("Old chain head: " + chainHead.header.toString()); LOG("Old chain head: " + chainHead.getHeader().toString());
LOG("New chain head: " + newStoredBlock.header.toString()); LOG("New chain head: " + newStoredBlock.getHeader().toString());
// TODO: Implement me! // TODO: Implement me!
// For each transaction we have to track which blocks they appeared in. Once a re-org takes place, // For each transaction we have to track which blocks they appeared in. Once a re-org takes place,