From 88212f6bfa5d9fe70655a74143de1000a03b5d72 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Sun, 27 Mar 2011 21:31:55 +0000 Subject: [PATCH] Make BlockStore and StoredBlock public. Move StoredBlock building into the class itself. --- src/com/google/bitcoin/core/BlockChain.java | 27 +++----- src/com/google/bitcoin/core/BlockStore.java | 2 +- .../bitcoin/core/BlockStoreException.java | 4 ++ .../google/bitcoin/core/MemoryBlockStore.java | 2 +- src/com/google/bitcoin/core/Peer.java | 2 +- src/com/google/bitcoin/core/StoredBlock.java | 61 +++++++++++++------ src/com/google/bitcoin/core/Wallet.java | 4 +- 7 files changed, 59 insertions(+), 43 deletions(-) diff --git a/src/com/google/bitcoin/core/BlockChain.java b/src/com/google/bitcoin/core/BlockChain.java index 9279a605..6b271147 100644 --- a/src/com/google/bitcoin/core/BlockChain.java +++ b/src/com/google/bitcoin/core/BlockChain.java @@ -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) diff --git a/src/com/google/bitcoin/core/BlockStore.java b/src/com/google/bitcoin/core/BlockStore.java index aefebd40..83199f87 100644 --- a/src/com/google/bitcoin/core/BlockStore.java +++ b/src/com/google/bitcoin/core/BlockStore.java @@ -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 diff --git a/src/com/google/bitcoin/core/BlockStoreException.java b/src/com/google/bitcoin/core/BlockStoreException.java index 38bc3370..cacb5716 100644 --- a/src/com/google/bitcoin/core/BlockStoreException.java +++ b/src/com/google/bitcoin/core/BlockStoreException.java @@ -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); } diff --git a/src/com/google/bitcoin/core/MemoryBlockStore.java b/src/com/google/bitcoin/core/MemoryBlockStore.java index 5b7de668..7c1a2b33 100644 --- a/src/com/google/bitcoin/core/MemoryBlockStore.java +++ b/src/com/google/bitcoin/core/MemoryBlockStore.java @@ -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); diff --git a/src/com/google/bitcoin/core/Peer.java b/src/com/google/bitcoin/core/Peer.java index 2e9952b1..981b288e 100644 --- a/src/com/google/bitcoin/core/Peer.java +++ b/src/com/google/bitcoin/core/Peer.java @@ -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); diff --git a/src/com/google/bitcoin/core/StoredBlock.java b/src/com/google/bitcoin/core/StoredBlock.java index 41b90309..ea524bfe 100644 --- a/src/com/google/bitcoin/core/StoredBlock.java +++ b/src/com/google/bitcoin/core/StoredBlock.java @@ -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); + } } diff --git a/src/com/google/bitcoin/core/Wallet.java b/src/com/google/bitcoin/core/Wallet.java index a7b94622..0a648bdb 100644 --- a/src/com/google/bitcoin/core/Wallet.java +++ b/src/com/google/bitcoin/core/Wallet.java @@ -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,