/**
* 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();
}
}