/** * Copyright 2011 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.bitcoin.core; import java.io.Serializable; import java.math.BigInteger; /** * Wraps a {@link Block} object with extra data that can be derived from the block chain but is slow or inconvenient to * calculate. By storing it alongside the block header we reduce the amount of work required significantly. * Recalculation is slow because the fields are cumulative - to find the chainWork you have to iterate over every * block in the chain back to the genesis block, which involves lots of seeking/loading etc. So we just keep a * running total: it's a disk space vs cpu/io tradeoff.<p> * * StoredBlocks are put inside a {@link BlockStore} which saves them to memory or disk. */ public class StoredBlock implements Serializable { private static final long serialVersionUID = -6097565241243701771L; private Block header; private BigInteger chainWork; private int height; public StoredBlock(Block header, BigInteger chainWork, int height) { 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. */ public boolean moreWorkThan(StoredBlock other) { return chainWork.compareTo(other.chainWork) > 0; } @Override public boolean equals(Object other) { if (!(other instanceof StoredBlock)) return false; StoredBlock o = (StoredBlock) other; return o.header.equals(header) && o.chainWork.equals(chainWork) && o.height == height; } @Override public int hashCode() { // A better hashCode is possible, but this works for now. return header.hashCode() ^ chainWork.hashCode() ^ 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.cloneAsHeader(), chainWork, height); } /** * Given a block store, looks up the previous block in this chain. Convenience method for doing * <tt>store.get(this.getHeader().getPrevBlockHash())</tt>. * * @return the previous block in the chain or null if it was not found in the store. */ public StoredBlock getPrev(BlockStore store) throws BlockStoreException { return store.get(getHeader().getPrevBlockHash()); } @Override public String toString() { return "Block at height " + getHeight() + ": " + getHeader().toString(); } }