package org.ripple.power.txns.btc;
import java.io.EOFException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.ripple.power.Helper;
/**
* <p>
* The Bitcoin block chain contains all of the transactions that have occurred
* and is available to everyone. The block chain consists of a series of blocks
* starting with the genesis block (block 0) and continuing to the chain head
* (the latest block in the chain).
* </p>
*
* <p>
* Each block is composed of one or more transactions. The first transaction is
* called the coinbase transaction and it assigns the block reward to the miner
* who solved the block hash. The remaining transactions move coins from Input A
* to Output B. A single transaction can contain multiple inputs and multiple
* outputs. The sum of the inputs minus the sum of the output represents the
* mining fee for that transaction.
* </p>
*
* <p>
* A block has the following format:
* </p>
*
* <pre>
* Size Field Description
* ==== ===== ===========
* 80 bytes BlockHeader Consists of 6 fields that are hashed to calculate the block hash
* VarInt TxCount Number of transactions in the block
* Variable Transactions The transactions in the block
* </pre>
*
* <p>
* The block header has the following format:
* </p>
*
* <pre>
* Size Field Description
* ==== ===== ===========
* 4 bytes Version The block version number
* 32 bytes PrevBlockHash The hash of the preceding block in the chain
* 32 byte MerkleRoot The Merkle root for the transactions in the block
* 4 bytes Time The time the block was mined
* 4 bytes Difficulty The target difficulty
* 4 bytes Nonce The nonce used to generate the required hash
* </pre>
*/
public class Block implements ByteSerializable {
/** The serialized byte stream */
private byte[] blockData;
/** The block version */
private int blockVersion;
/** The block hash calculated from the block header */
private Sha256Hash blockHash;
/** The hash for the previous block in the chain */
private Sha256Hash prevBlockHash;
/** The Merkle root for the transactions in the block */
private Sha256Hash merkleRoot;
/** The Merkle tree for the transaction in the block */
private List<byte[]> merkleTree;
/** The block timestamp */
private long timeStamp;
/** The target difficulty */
private long targetDifficulty;
/** The nonce */
private int nonce;
/** The transactions contained in the block */
private List<Transaction> transactions;
/**
* Create an empty block for use by subclasses
*/
protected Block() {
}
/**
* Create a block from a serialized byte array
*
* @param inBytes
* Byte array containing the serialized data
* @param doVerify
* TRUE if the block structure should be verified
* @throws EOFException
* End-of-data while processing byte stream
* @throws VerificationException
* Block verification failed
*/
public Block(byte[] inBytes, boolean doVerify) throws EOFException,
VerificationException {
this(inBytes, 0, inBytes.length, doVerify);
}
/**
* Create a block from a serialized byte array
*
* @param inBytes
* Byte array containing the serialized data
* @param inOffset
* Starting offset within the array
* @param inLength
* Length of the serialized data
* @param doVerify
* TRUE if the block structure should be verified
* @throws EOFException
* Serialized byte stream is too short
* @throws VerificationException
* Block verification failed
*/
public Block(byte[] inBytes, int inOffset, int inLength, boolean doVerify)
throws EOFException, VerificationException {
this(new SerializedBuffer(inBytes, inOffset, inLength), doVerify);
}
/**
* Create a block from a serialized buffer
*
* @param inBuffer
* Serialized buffer
* @param doVerify
* TRUE if the block structure should be verified
* @throws EOFException
* Serialized byte stream is too short
* @throws VerificationException
* Block verification failed
*/
public Block(SerializedBuffer inBuffer, boolean doVerify)
throws EOFException, VerificationException {
//
// We must have at least 80 bytes
//
if (inBuffer.available() < BlockHeader.HEADER_SIZE)
throw new EOFException("Block header truncated");
//
// Compute the block hash from the serialized block header
//
int startPosition = inBuffer.getPosition();
blockHash = new Sha256Hash(Helper.reverseBytes(Helper
.doubleDigest(inBuffer.getBytes(BlockHeader.HEADER_SIZE))));
inBuffer.setPosition(startPosition);
//
// Read the block header
//
readHeader(inBuffer);
//
// Read the transactions
//
readTransactions(inBuffer);
//
// Verify the block and its transactions. Note that transaction
// signatures and connected
// outputs will be verified when the block is added to the block chain.
//
if (doVerify)
verifyBlock();
//
// Save a copy of the serialized byte stream
//
inBuffer.setSegmentStart(startPosition);
blockData = inBuffer.getSegmentBytes();
}
/**
* Write the serialized block data to the output buffer
*
* @param outBuffer
* Output buffer
* @return Output buffer
*/
@Override
public SerializedBuffer getBytes(SerializedBuffer outBuffer) {
outBuffer.putBytes(blockData);
return outBuffer;
}
/**
* Return the serialized block data
*
* @return Byte array containing the serialized block
*/
@Override
public byte[] getBytes() {
return blockData;
}
/**
* Write the serialized block header to the output buffer
*
* @param outBuffer
* Output buffer
* @return Output buffer
*/
public SerializedBuffer getHeaderBytes(SerializedBuffer outBuffer) {
outBuffer.putBytes(blockData, 0, BlockHeader.HEADER_SIZE);
return outBuffer;
}
/**
* Return the serialized block header
*
* @return Byte array containing just the block header
*/
public byte[] getHeaderBytes() {
return Arrays.copyOfRange(blockData, 0, BlockHeader.HEADER_SIZE);
}
/**
* <p>
* Returns the block version. Only Version 1 and Version 2 blocks are
* supported.
* </p>
* <ul>
* <li>Blocks created before BIP 34 are Version 1 and do not contain the
* chain height in the coinbase transaction input script</li>
* <li>Blocks created after BIP 34 are Version 2 and contain the chain
* height in the coinbase transaction input script</li>
* </ul>
*
* @return Block version
*/
public int getVersion() {
return blockVersion;
}
/**
* Returns the time the block was mined
*
* @return The block timestamp in seconds since the Unix epoch (Jan 1, 1970)
*/
public long getTimeStamp() {
return timeStamp;
}
/**
* Returns the block hash calculated over the block header
*
* @return Block hash
*/
public Sha256Hash getHash() {
return blockHash;
}
/**
* Returns the block hash as a formatted hex string
*
* @return Hex string
*/
public String getHashAsString() {
return blockHash.toString();
}
/**
* Returns the hash of the previous block in the chain
*
* @return Previous block hash
*/
public Sha256Hash getPrevBlockHash() {
return prevBlockHash;
}
/**
* Returns the Merkle root
*
* @return Merkle root
*/
public Sha256Hash getMerkleRoot() {
return merkleRoot;
}
/**
* Returns the Merkle tree
*
* @return Merkle tree
*/
public List<byte[]> getMerkleTree() {
if (merkleTree == null)
merkleTree = buildMerkleTree();
return merkleTree;
}
/**
* Returns the target difficulty in compact form
*
* @return Target difficulty
*/
public long getTargetDifficulty() {
return targetDifficulty;
}
/**
* Returns the target difficulty as a 256-bit value that can be compared to
* a SHA-256 hash. Inside a block. the target is represented using the
* compact form.
*
* @return The difficulty target
*/
public BigInteger getTargetDifficultyAsInteger() {
return Helper.decodeCompactBits(targetDifficulty);
}
/**
* Returns the work represented by this block
*
* Work is defined as the number of tries needed to solve a block in the
* average case. As the target gets lower, the amount of work goes up.
*
* @return The work represented by this block
*/
public BigInteger getWork() {
BigInteger target = getTargetDifficultyAsInteger();
return BlockHeader.LARGEST_HASH.divide(target.add(BigInteger.ONE));
}
/**
* Returns the block nonce
*
* @return Block nonce
*/
public int getNonce() {
return nonce;
}
/**
* Returns the transactions in this block
*
* @return Transaction list
*/
public List<Transaction> getTransactions() {
return transactions;
}
/**
* Calculates the Merkle root from the block transactions
*
* @return Merkle root
*/
private Sha256Hash calculateMerkleRoot() {
if (merkleTree == null)
merkleTree = buildMerkleTree();
return new Sha256Hash(merkleTree.get(merkleTree.size() - 1));
}
/**
* Builds the Merkle tree from the block transactions
*
* @return List of byte arrays representing the nodes in the Merkle tree
*/
private List<byte[]> buildMerkleTree() {
//
// The Merkle root is based on a tree of hashes calculated from the
// transactions:
//
// root
// / \
// A B
// / \ / \
// t1 t2 t3 t4
//
// The tree is represented as a list: t1,t2,t3,t4,A,B,root where each
// entry is a hash
//
// The hashing algorithm is double SHA-256. The leaves are a hash of the
// serialized contents of the transaction.
// The interior nodes are hashes of the concatenation of the two child
// hashes.
//
// This structure allows the creation of proof that a transaction was
// included into a block without having to
// provide the full block contents. Instead, you can provide only a
// Merkle branch. For example to prove tx2 was
// in a block you can just provide tx2, the hash(tx1) and B. Now the
// other party has everything they need to
// derive the root, which can be checked against the block header. These
// proofs are useful when we
// want to download partial block contents.
//
// Note that if the number of transactions is not even, the last tx is
// repeated to make it so.
// A tree with 5 transactions would look like this:
//
// root
// / \
// 4 5
// / \ / \
// 1 2 3 3
// / \ / \ / \
// t1 t2 t3 t4 t5 t5
//
ArrayList<byte[]> tree = new ArrayList<>();
for (Transaction tx : transactions) {
tree.add(tx.getHash().getBytes());
}
//
// The tree is generated starting at the leaves and moving down to the
// root
//
int levelOffset = 0;
//
// Step through each level, stopping when we reach the root (levelSize
// == 1).
//
for (int levelSize = transactions.size(); levelSize > 1; levelSize = (levelSize + 1) / 2) {
//
// Process each pair of nodes on the current level
//
for (int left = 0; left < levelSize; left += 2) {
//
// The right hand node can be the same as the left hand in the
// case where we have
// an odd number of nodes for the level
//
int right = Math.min(left + 1, levelSize - 1);
byte[] leftBytes = Helper.reverseBytes(tree.get(levelOffset
+ left));
byte[] rightBytes = Helper.reverseBytes(tree.get(levelOffset
+ right));
byte[] nodeHash = Helper.doubleDigestTwoBuffers(leftBytes, 0,
32, rightBytes, 0, 32);
tree.add(Helper.reverseBytes(nodeHash));
}
//
// Move to the next level.
//
levelOffset += levelSize;
}
return tree;
}
/**
* Reads the block header from the input stream
*
* @param inBuffer
* Input buffer
* @throws EOFException
* Serialized input stream is too short
* @throws VerificationException
* Block structure is incorrect
*/
private void readHeader(SerializedBuffer inBuffer) throws EOFException,
VerificationException {
blockVersion = inBuffer.getInt();
if (blockVersion < 1 || blockVersion > 3)
throw new VerificationException(String.format(
"Block version %d is not supported", blockVersion));
prevBlockHash = new Sha256Hash(
Helper.reverseBytes(inBuffer.getBytes(32)));
merkleRoot = new Sha256Hash(Helper.reverseBytes(inBuffer.getBytes(32)));
timeStamp = inBuffer.getUnsignedInt();
targetDifficulty = inBuffer.getUnsignedInt();
nonce = inBuffer.getInt();
}
/**
* Reads the transactions from the serialized stream
*
* @param inBuffer
* Serialized buffer
* @throws EOFException
* Serialized input stream is too short
* @throws VerificationException
* Transaction verification failed
*/
private void readTransactions(SerializedBuffer inBuffer)
throws EOFException, VerificationException {
int count = inBuffer.getVarInt();
if (count < 1 || count > NetParams.MAX_BLOCK_SIZE / 60)
throw new VerificationException(String.format(
"Transaction count %d is not valid", count));
transactions = new ArrayList<>(count);
for (int i = 0; i < count; i++)
transactions.add(new Transaction(inBuffer));
}
/**
* <p>
* Checks the block to ensure it follows the rules laid out in the network
* parameters.
* </p>
* <p>
* The following checks are performed:
* </p>
* <ul>
* <li>Check the proof of work by comparing the block hash to the target
* difficulty</li>
* <li>Check the timestamp against the current time</li>
* <li>Verify that there is a single coinbase transaction and it is the
* first transaction in the block</li>
* <li>Verify the merkle root</li>
* <li>Verify the transaction structure</li>
* <li>Verify the transaction lock time</li>
* </ul>
*
* @throws VerificationException
* Block verification failed
*/
private void verifyBlock() throws VerificationException {
//
// Ensure this block does in fact represent real work done. If the
// difficulty is high enough,
// we can be fairly certain the work was done by the network.
//
// The block hash must be less than or equal to the target difficulty
// (the difficulty increases
// by requiring an increasing number of leading zeroes in the block
// hash)
//
BigInteger target = getTargetDifficultyAsInteger();
if (target.signum() <= 0
|| target.compareTo(NetParams.PROOF_OF_WORK_LIMIT) > 0)
throw new VerificationException("Target difficulty is not valid",
RejectMessage.REJECT_INVALID, blockHash);
BigInteger hash = getHash().toBigInteger();
if (hash.compareTo(target) > 0)
throw new VerificationException(
"Block hash is higher than target difficulty",
RejectMessage.REJECT_INVALID, blockHash);
//
// Verify the block timestamp
//
long currentTime = System.currentTimeMillis() / 1000;
if (timeStamp > currentTime + NetParams.ALLOWED_TIME_DRIFT)
throw new VerificationException(
"Block timestamp is too far in the future",
RejectMessage.REJECT_INVALID, blockHash);
//
// Check that there is just one coinbase transaction and it is the first
// transaction in the block
//
boolean foundCoinBase = false;
for (Transaction tx : transactions) {
if (tx.isCoinBase()) {
if (foundCoinBase)
throw new VerificationException(
"Block contains multiple coinbase transactions",
RejectMessage.REJECT_MALFORMED, blockHash);
foundCoinBase = true;
} else if (!foundCoinBase) {
throw new VerificationException(
"First transaction in block is not the coinbase transaction",
RejectMessage.REJECT_MALFORMED, blockHash);
}
}
//
// Verify the Merkle root
//
Sha256Hash checkRoot = calculateMerkleRoot();
if (!checkRoot.equals(merkleRoot))
throw new VerificationException("Merkle root is not correct",
RejectMessage.REJECT_INVALID, blockHash);
//
// Verify the transactions in the block
//
for (Transaction tx : transactions) {
//
// Verify the transaction structure
//
tx.verify(false);
//
// A transaction is locked if the lock time is greater than the
// block time (we allow
// a 10-minute leeway)
//
if (tx.getLockTime() > timeStamp + (10 * 60)) {
//
// A transaction is unlocked if all of the input sequences are
// -1 even though
// the lock time has not been reached
//
List<TransactionInput> txInputs = tx.getInputs();
for (TransactionInput txInput : txInputs) {
if (txInput.getSeqNumber() != -1)
throw new VerificationException(
"Transaction lock time greater than block time",
RejectMessage.REJECT_INVALID, tx.getHash());
}
}
}
}
/**
* Determines if this block is equal to another block
*
* @param obj
* The block to compare
* @return TRUE if the blocks are equal
*/
@Override
public boolean equals(Object obj) {
return (obj != null && (obj instanceof Block) && blockHash
.equals(((Block) obj).blockHash));
}
/**
* Returns the hash code for this object. The returned value is based on the
* block hash but is not the same value.
*
* @return Hash code
*/
@Override
public int hashCode() {
return blockHash.hashCode();
}
/**
* Returns a string representation for this block
*
* @return Formatted string
*/
@Override
public String toString() {
return String
.format("Block hash: %s\n Previous block hash %s\n Merkle root: %s\n Target difficulty %d",
getHashAsString(), getPrevBlockHash().toString(),
getMerkleRoot().toString(), targetDifficulty);
}
}