/** Returns a copy of the block, but without any transactions. */ public Block cloneAsHeader() { maybeParseHeader(); Block block = new Block(params); block.nonce = nonce; block.prevBlockHash = prevBlockHash.duplicate(); block.merkleRoot = getMerkleRoot().duplicate(); block.version = version; block.time = time; block.difficultyTarget = difficultyTarget; block.transactions = null; block.hash = getHash().duplicate(); return block; }
/** * Returns a solved block that builds on top of this one. This exists for unit tests. In this * variant you can specify a public key (pubkey) for use in generating coinbase blocks. */ Block createNextBlock( Address to, TransactionOutPoint prevOut, long time, byte[] pubKey, BigInteger coinbaseValue) { Block b = new Block(params); b.setDifficultyTarget(difficultyTarget); b.addCoinbaseTransaction(pubKey, coinbaseValue); if (to != null) { // Add a transaction paying 50 coins to the "to" address. Transaction t = new Transaction(params); t.addOutput(new TransactionOutput(params, t, Utils.toNanoCoins(50, 0), to)); // The input does not really need to be a valid signature, as long as it has the right general // form. TransactionInput input; if (prevOut == null) { input = new TransactionInput(params, t, Script.createInputScript(EMPTY_BYTES, EMPTY_BYTES)); // Importantly the outpoint hash cannot be zero as that's how we detect a coinbase // transaction in isolation // but it must be unique to avoid 'different' transactions looking the same. byte[] counter = new byte[32]; counter[0] = (byte) txCounter++; counter[1] = 1; input.getOutpoint().setHash(new Sha256Hash(counter)); } else { input = new TransactionInput( params, t, Script.createInputScript(EMPTY_BYTES, EMPTY_BYTES), prevOut); } t.addInput(input); b.addTransaction(t); } b.setPrevBlockHash(getHash()); // Don't let timestamp go backwards if (getTimeSeconds() >= time) b.setTime(getTimeSeconds() + 1); else b.setTime(time); b.solve(); try { b.verifyHeader(); } catch (VerificationException e) { throw new RuntimeException(e); // Cannot happen. } return b; }
@Override protected TransactionOutputChanges connectTransactions(int height, Block block) throws VerificationException, BlockStoreException { checkState(lock.isLocked()); if (block.transactions == null) throw new RuntimeException( "connectTransactions called with Block that didn't have transactions!"); if (!params.passesCheckpoint(height, block.getHash())) throw new VerificationException("Block failed checkpoint lockin at " + height); blockStore.beginDatabaseBatchWrite(); LinkedList<StoredTransactionOutput> txOutsSpent = new LinkedList<StoredTransactionOutput>(); LinkedList<StoredTransactionOutput> txOutsCreated = new LinkedList<StoredTransactionOutput>(); long sigOps = 0; final boolean enforceBIP16 = block.getTimeSeconds() >= NetworkParameters.BIP16_ENFORCE_TIME; if (scriptVerificationExecutor.isShutdown()) scriptVerificationExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); List<Future<VerificationException>> listScriptVerificationResults = new ArrayList<Future<VerificationException>>(block.transactions.size()); try { if (!params.isCheckpoint(height)) { // BIP30 violator blocks are ones that contain a duplicated transaction. They are all in the // checkpoints list and we therefore only check non-checkpoints for duplicated transactions // here. See the // BIP30 document for more details on this: https://en.litecoin.it/wiki/BIP_0030 for (Transaction tx : block.transactions) { Sha256Hash hash = tx.getHash(); // If we already have unspent outputs for this hash, we saw the tx already. Either the // block is // being added twice (bug) or the block is a BIP30 violator. if (blockStore.hasUnspentOutputs(hash, tx.getOutputs().size())) throw new VerificationException("Block failed BIP30 test!"); if (enforceBIP16) // We already check non-BIP16 sigops in Block.verifyTransactions(true) sigOps += tx.getSigOpCount(); } } BigInteger totalFees = BigInteger.ZERO; BigInteger coinbaseValue = null; for (Transaction tx : block.transactions) { boolean isCoinBase = tx.isCoinBase(); BigInteger valueIn = BigInteger.ZERO; BigInteger valueOut = BigInteger.ZERO; if (!isCoinBase) { // For each input of the transaction remove the corresponding output from the set of // unspent // outputs. for (int index = 0; index < tx.getInputs().size(); index++) { TransactionInput in = tx.getInputs().get(index); StoredTransactionOutput prevOut = blockStore.getTransactionOutput( in.getOutpoint().getHash(), in.getOutpoint().getIndex()); if (prevOut == null) throw new VerificationException( "Attempted to spend a non-existent or already spent output!"); // Coinbases can't be spent until they mature, to avoid re-orgs destroying entire // transaction // chains. The assumption is there will ~never be re-orgs deeper than the spendable // coinbase // chain depth. if (height - prevOut.getHeight() < params.getSpendableCoinbaseDepth()) throw new VerificationException( "Tried to spend coinbase at depth " + (height - prevOut.getHeight())); // TODO: Check we're not spending the genesis transaction here. Satoshis code won't // allow it. valueIn = valueIn.add(prevOut.getValue()); if (enforceBIP16) { if (new Script(params, prevOut.getScriptBytes(), 0, prevOut.getScriptBytes().length) .isPayToScriptHash()) sigOps += Script.getP2SHSigOpCount(in.getScriptBytes()); if (sigOps > Block.MAX_BLOCK_SIGOPS) throw new VerificationException("Too many P2SH SigOps in block"); } // All of these copies are terribly ugly, however without them, // I see some odd concurrency issues where scripts throw exceptions // (mostly "Attempted OP_* on empty stack" or similar) when they shouldn't. // In my tests, total time spent in net.usecredits.credits.core when // downloading the chain is < 0.5%, so doing this is no big efficiency issue. // TODO: Find out the underlying issue and create a better work-around final int currentIndex = index; final Transaction txCache; try { txCache = new Transaction(params, tx.unsafeLitecoinSerialize()); } catch (ProtocolException e1) { throw new RuntimeException(e1); } final Script scriptSig = in.getScriptSig(); final Script scriptPubKey = new Script(params, prevOut.getScriptBytes(), 0, prevOut.getScriptBytes().length); FutureTask<VerificationException> future = new FutureTask<VerificationException>( new Callable<VerificationException>() { public VerificationException call() { try { scriptSig.correctlySpends( txCache, currentIndex, scriptPubKey, enforceBIP16); } catch (VerificationException e) { return e; } return null; } }); scriptVerificationExecutor.execute(future); listScriptVerificationResults.add(future); // in.getScriptSig().correctlySpends(tx, index, new Script(params, // prevOut.getScriptBytes(), 0, prevOut.getScriptBytes().length)); blockStore.removeUnspentTransactionOutput(prevOut); txOutsSpent.add(prevOut); } } Sha256Hash hash = tx.getHash(); for (TransactionOutput out : tx.getOutputs()) { valueOut = valueOut.add(out.getValue()); // For each output, add it to the set of unspent outputs so it can be consumed in future. StoredTransactionOutput newOut = new StoredTransactionOutput( hash, out.getIndex(), out.getValue(), height, isCoinBase, out.getScriptBytes()); blockStore.addUnspentTransactionOutput(newOut); txOutsCreated.add(newOut); } // All values were already checked for being non-negative (as it is verified in // Transaction.verify()) // but we check again here just for defence in depth. Transactions with zero output value // are OK. if (valueOut.compareTo(BigInteger.ZERO) < 0 || valueOut.compareTo(params.MAX_MONEY) > 0) throw new VerificationException("Transaction output value out of rage"); if (isCoinBase) { coinbaseValue = valueOut; } else { if (valueIn.compareTo(valueOut) < 0 || valueIn.compareTo(params.MAX_MONEY) > 0) throw new VerificationException("Transaction input value out of range"); totalFees = totalFees.add(valueIn.subtract(valueOut)); } } if (totalFees.compareTo(params.MAX_MONEY) > 0 || block.getBlockInflation(height).add(totalFees).compareTo(coinbaseValue) < 0) throw new VerificationException("Transaction fees out of range"); for (Future<VerificationException> future : listScriptVerificationResults) { VerificationException e; try { e = future.get(); } catch (InterruptedException thrownE) { throw new RuntimeException(thrownE); // Shouldn't happen } catch (ExecutionException thrownE) { log.error("Script.correctlySpends threw a non-normal exception: " + thrownE.getCause()); throw new VerificationException( "Bug in Script.correctlySpends, likely script malformed in some new and interesting way."); } if (e != null) throw e; } } catch (VerificationException e) { scriptVerificationExecutor.shutdownNow(); blockStore.abortDatabaseBatchWrite(); throw e; } catch (BlockStoreException e) { scriptVerificationExecutor.shutdownNow(); blockStore.abortDatabaseBatchWrite(); throw e; } return new TransactionOutputChanges(txOutsCreated, txOutsSpent); }
@Override public boolean equals(Object o) { if (!(o instanceof Block)) return false; Block other = (Block) o; return getHash().equals(other.getHash()); }