package com.google.devcoin.core; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Created by jagdeep.sidhu on 1/5/14. */ public class BlockMergeMinedPayload { private static final Logger log = LoggerFactory.getLogger(BlockMergeMinedPayload.class); public transient int cursor; transient NetworkParameters params; // Merged mining fields //Parent Block TX public transient Transaction parentBlockCoinBaseTx; public transient Block block; private transient byte bytes[]; public transient int length; //Coinbase Link public transient Sha256Hash hashOfParentBlockHeader; //Parent Block Header public transient Block parentBlockHeader; private transient boolean parsed; public BlockMergeMinedPayload(NetworkParameters parameters, byte[] payloadBytes, int cursorStart, Block block) throws ProtocolException { parsed = false; bytes = payloadBytes; this.block = block; this.params = parameters; if(bytes != null) parse(cursorStart); bytes = null; } void parse(int cursorStart) throws ProtocolException { length = 0; parsed = false; cursor = cursorStart; parseMergedMineInfo(); length = cursor-cursorStart; cursor = cursorStart; if(length > 0) { parsed = true; } } public boolean IsValid() { return parsed; } private void parseMergedMineInfo() throws ProtocolException { if(bytes == null || bytes.length <= (Block.HEADER_SIZE+1)) { log.info("Warning: Trying to parse merged-mine info from information passed in that doesn't include merged-mine information, skipping..."); return; } // Parent Block Coinbase Transaction: parentBlockCoinBaseTx = new Transaction(params, bytes, cursor, this.block, false, false, Block.UNKNOWN_LENGTH); parentBlockCoinBaseTx.getConfidence().setSource(TransactionConfidence.Source.NETWORK); cursor += parentBlockCoinBaseTx.getMessageSize(); // Coinbase Link: // Hash of parent block header hashOfParentBlockHeader = readHash(); // Number of links in branch long numHashes = readVarInt(); // Hash #1 - #numHashes cursor += 32*numHashes; // Branch sides bitmask cursor += 4; // Aux Blockchain Link: // Number of links in branch numHashes = readVarInt(); // Hash #1 - #numHashes cursor += 32*numHashes; // Branch sides bitmask cursor += 4; byte[] header = readBytes(80); // Parent Block Header: parentBlockHeader = new Block(this.params, null, header, false, false, 80, 0); // reads in the block information as needed Sha256Hash hashOfParentBlockHeaderCalculated = parentBlockHeader.calculateHash(); String str = parentBlockCoinBaseTx.toString(); String txHash = parentBlockCoinBaseTx.getHashAsString(); /*Note that the block_hash element is not needed as you have the full parent_block header element and can calculate the hash from that. The current Namecoin client doesn't check this field for validity, and as such some AuxPOW blocks have it little-endian, and some have it big-endian. */ /*https://en.bitcoin.it/wiki/Merged_mining_specification*/ if(!hashOfParentBlockHeader.equals(hashOfParentBlockHeaderCalculated)) { Sha256Hash reversedHashOfParentBlockHeader = new Sha256Hash(Utils.reverseBytes(hashOfParentBlockHeader.getBytes())); if(!reversedHashOfParentBlockHeader.equals(hashOfParentBlockHeaderCalculated)){ throw new ProtocolException("Hash of parent block header calculated does not match hash of parent block header received in merged-mining header."); } else { hashOfParentBlockHeader = reversedHashOfParentBlockHeader; } } } /** * Returns a multi-line string containing a description of the contents of * the block. Use for debugging purposes only. */ long readVarInt() throws ProtocolException { return readVarInt(0); } long readVarInt(int offset) throws ProtocolException { try { VarInt varint = new VarInt(bytes, cursor + offset); cursor += offset + varint.getOriginalSizeInBytes(); return varint.value; } catch (ArrayIndexOutOfBoundsException e) { throw new ProtocolException(e); } } public String toString() { StringBuilder s = new StringBuilder(""); s.append(" parent block coin base transaction: \n"); s.append(parentBlockCoinBaseTx.toString()); s.append("\n"); if(hashOfParentBlockHeader != null) { s.append(" coinbase link: \n"); s.append(" hash of parent block header: "); s.append(hashOfParentBlockHeader); s.append("\n"); } if(parentBlockHeader != null) { s.append(" parent block header: \n"); s.append(parentBlockHeader.toString()); s.append("\n"); } return s.toString(); } byte[] readBytes(int length) throws ProtocolException { try { byte[] b = new byte[length]; System.arraycopy(bytes, cursor, b, 0, length); cursor += length; return b; } catch (IndexOutOfBoundsException e) { throw new ProtocolException(e); } } long readInt64() throws ProtocolException { try { long u = Utils.readInt64(bytes, cursor); cursor += 8; return u; } catch (ArrayIndexOutOfBoundsException e) { throw new ProtocolException(e); } } long readUint32() throws ProtocolException { try { long u = Utils.readUint32(bytes, cursor); cursor += 4; return u; } catch (ArrayIndexOutOfBoundsException e) { throw new ProtocolException(e); } } Sha256Hash readHash() throws ProtocolException { try { byte[] hash = new byte[32]; System.arraycopy(bytes, cursor, hash, 0, 32); // We have to flip it around, as it's been read off the wire in little endian. // Not the most efficient way to do this but the clearest. hash = Utils.reverseBytes(hash); cursor += 32; return new Sha256Hash(hash); } catch (IndexOutOfBoundsException e) { throw new ProtocolException(e); } } }