All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.bitcoin.core.StoredBlock Maven / Gradle / Ivy

The newest version!
/**
 * 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 com.google.bitcoin.store.BlockStore;
import com.google.bitcoin.store.BlockStoreException;

import java.io.*;
import java.math.BigInteger;
import java.nio.ByteBuffer;

import static com.google.common.base.Preconditions.checkState;

/**
 * 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.

* * 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; // A BigInteger representing the total amount of work done so far on this chain. As of May 2011 it takes 8 // bytes to represent this field, so 12 bytes should be plenty for now. public static final int CHAIN_WORK_BYTES = 12; public static final byte[] EMPTY_BYTES = new byte[CHAIN_WORK_BYTES]; public static final int COMPACT_SERIALIZED_SIZE = Block.HEADER_SIZE + CHAIN_WORK_BYTES + 4; // for height 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, chainWork, height); } /** * Given a block store, looks up the previous block in this chain. Convenience method for doing * store.get(this.getHeader().getPrevBlockHash()). * * @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()); } /** Serializes the stored block to a custom packed format. Used by {@link CheckpointManager}. */ public void serializeCompact(ByteBuffer buffer) { byte[] chainWorkBytes = getChainWork().toByteArray(); checkState(chainWorkBytes.length <= CHAIN_WORK_BYTES, "Ran out of space to store chain work!"); if (chainWorkBytes.length < CHAIN_WORK_BYTES) { // Pad to the right size. buffer.put(EMPTY_BYTES, 0, CHAIN_WORK_BYTES - chainWorkBytes.length); } buffer.put(chainWorkBytes); buffer.putInt(getHeight()); // Using unsafeBitcoinSerialize here can give us direct access to the same bytes we read off the wire, // avoiding serialization round-trips. byte[] bytes = getHeader().unsafeBitcoinSerialize(); buffer.put(bytes, 0, Block.HEADER_SIZE); // Trim the trailing 00 byte (zero transactions). } /** De-serializes the stored block from a custom packed format. Used by {@link CheckpointManager}. */ public static StoredBlock deserializeCompact(NetworkParameters params, ByteBuffer buffer) throws ProtocolException { byte[] chainWorkBytes = new byte[StoredBlock.CHAIN_WORK_BYTES]; buffer.get(chainWorkBytes); BigInteger chainWork = new BigInteger(1, chainWorkBytes); int height = buffer.getInt(); // +4 bytes byte[] header = new byte[Block.HEADER_SIZE + 1]; // Extra byte for the 00 transactions length. buffer.get(header, 0, Block.HEADER_SIZE); return new StoredBlock(new Block(params, header), chainWork, height); } @Override public String toString() { return String.format("Block %s at height %d: %s", getHeader().getHashAsString(), getHeight(), getHeader().toString()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy