private WalletTransaction connectTransactionOutputs( org.bitcoinj.wallet.Protos.Transaction txProto) throws UnreadableWalletException { Transaction tx = txMap.get(txProto.getHash()); final WalletTransaction.Pool pool; switch (txProto.getPool()) { case DEAD: pool = WalletTransaction.Pool.DEAD; break; case PENDING: pool = WalletTransaction.Pool.PENDING; break; case SPENT: pool = WalletTransaction.Pool.SPENT; break; case UNSPENT: pool = WalletTransaction.Pool.UNSPENT; break; // Upgrade old wallets: inactive pool has been merged with the pending pool. // Remove this some time after 0.9 is old and everyone has upgraded. // There should not be any spent outputs in this tx as old wallets would not allow them to // be spent // in this state. case INACTIVE: case PENDING_INACTIVE: pool = WalletTransaction.Pool.PENDING; break; default: throw new UnreadableWalletException("Unknown transaction pool: " + txProto.getPool()); } for (int i = 0; i < tx.getOutputs().size(); i++) { TransactionOutput output = tx.getOutputs().get(i); final Protos.TransactionOutput transactionOutput = txProto.getTransactionOutput(i); if (transactionOutput.hasSpentByTransactionHash()) { final ByteString spentByTransactionHash = transactionOutput.getSpentByTransactionHash(); Transaction spendingTx = txMap.get(spentByTransactionHash); if (spendingTx == null) { throw new UnreadableWalletException( String.format( "Could not connect %s to %s", tx.getHashAsString(), byteStringToHash(spentByTransactionHash))); } final int spendingIndex = transactionOutput.getSpentByTransactionIndex(); TransactionInput input = checkNotNull(spendingTx.getInput(spendingIndex)); input.connect(output); } } if (txProto.hasConfidence()) { Protos.TransactionConfidence confidenceProto = txProto.getConfidence(); TransactionConfidence confidence = tx.getConfidence(); readConfidence(tx, confidenceProto, confidence); } return new WalletTransaction(pool, tx); }
@Test public void skipScripts() throws Exception { store = createStore(params, 10); chain = new FullPrunedBlockChain(params, store); // Check that we aren't accidentally leaving any references // to the full StoredUndoableBlock's lying around (ie memory leaks) ECKey outKey = new ECKey(); int height = 1; // Build some blocks on genesis block to create a spendable output Block rollingBlock = params .getGenesisBlock() .createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++); chain.add(rollingBlock); TransactionOutput spendableOutput = rollingBlock.getTransactions().get(0).getOutput(0); for (int i = 1; i < params.getSpendableCoinbaseDepth(); i++) { rollingBlock = rollingBlock.createNextBlockWithCoinbase( Block.BLOCK_VERSION_GENESIS, outKey.getPubKey(), height++); chain.add(rollingBlock); } rollingBlock = rollingBlock.createNextBlock(null); Transaction t = new Transaction(params); t.addOutput(new TransactionOutput(params, t, FIFTY_COINS, new byte[] {})); TransactionInput input = t.addInput(spendableOutput); // Invalid script. input.clearScriptBytes(); rollingBlock.addTransaction(t); rollingBlock.solve(); chain.setRunScripts(false); try { chain.add(rollingBlock); } catch (VerificationException e) { fail(); } try { store.close(); } catch (Exception e) { } }
/** * Closes this channel and broadcasts the highest value payment transaction on the network. * * <p>This will set the state to {@link State#CLOSED} if the transaction is successfully broadcast * on the network. If we fail to broadcast for some reason, the state is set to {@link * State#ERROR}. * * <p>If the current state is before {@link State#READY} (ie we have not finished initializing the * channel), we simply set the state to {@link State#CLOSED} and let the client handle getting its * refund transaction confirmed. * * @return a future which completes when the provided multisig contract successfully broadcasts, * or throws if the broadcast fails for some reason. Note that if the network simply rejects * the transaction, this future will never complete, a timeout should be used. * @throws InsufficientMoneyException If the payment tx would have cost more in fees to spend than * it is worth. */ public synchronized ListenableFuture<Transaction> close() throws InsufficientMoneyException { if (storedServerChannel != null) { StoredServerChannel temp = storedServerChannel; storedServerChannel = null; StoredPaymentChannelServerStates channels = (StoredPaymentChannelServerStates) wallet.getExtensions().get(StoredPaymentChannelServerStates.EXTENSION_ID); channels.closeChannel( temp); // May call this method again for us (if it wasn't the original caller) if (state.compareTo(State.CLOSING) >= 0) return closedFuture; } if (state.ordinal() < State.READY.ordinal()) { log.error("Attempt to settle channel in state " + state); state = State.CLOSED; closedFuture.set(null); return closedFuture; } if (state != State.READY) { // TODO: What is this codepath for? log.warn("Failed attempt to settle a channel in state " + state); return closedFuture; } Transaction tx = null; try { Wallet.SendRequest req = makeUnsignedChannelContract(bestValueToMe); tx = req.tx; // Provide a throwaway signature so that completeTx won't complain out about unsigned inputs // it doesn't // know how to sign. Note that this signature does actually have to be valid, so we can't use // a dummy // signature to save time, because otherwise completeTx will try to re-sign it to make it // valid and then // die. We could probably add features to the SendRequest API to make this a bit more // efficient. signMultisigInput(tx, Transaction.SigHash.NONE, true); // Let wallet handle adding additional inputs/fee as necessary. req.shuffleOutputs = false; req.missingSigsMode = Wallet.MissingSigsMode.USE_DUMMY_SIG; wallet.completeTx(req); // TODO: Fix things so shuffling is usable. feePaidForPayment = req.tx.getFee(); log.info("Calculated fee is {}", feePaidForPayment); if (feePaidForPayment.compareTo(bestValueToMe) > 0) { final String msg = String.format( Locale.US, "Had to pay more in fees (%s) than the channel was worth (%s)", feePaidForPayment, bestValueToMe); throw new InsufficientMoneyException(feePaidForPayment.subtract(bestValueToMe), msg); } // Now really sign the multisig input. signMultisigInput(tx, Transaction.SigHash.ALL, false); // Some checks that shouldn't be necessary but it can't hurt to check. tx.verify(); // Sanity check syntax. for (TransactionInput input : tx.getInputs()) input.verify(); // Run scripts and ensure it is valid. } catch (InsufficientMoneyException e) { throw e; // Don't fall through. } catch (Exception e) { log.error( "Could not verify self-built tx\nMULTISIG {}\nCLOSE {}", multisigContract, tx != null ? tx : ""); throw new RuntimeException(e); // Should never happen. } state = State.CLOSING; log.info("Closing channel, broadcasting tx {}", tx); // The act of broadcasting the transaction will add it to the wallet. ListenableFuture<Transaction> future = broadcaster.broadcastTransaction(tx).future(); Futures.addCallback( future, new FutureCallback<Transaction>() { @Override public void onSuccess(Transaction transaction) { log.info("TX {} propagated, channel successfully closed.", transaction.getHash()); state = State.CLOSED; closedFuture.set(transaction); } @Override public void onFailure(Throwable throwable) { log.error("Failed to settle channel, could not broadcast", throwable); state = State.ERROR; closedFuture.setException(throwable); } }); return closedFuture; }
/** * This is required for signatures which use a sigHashType which cannot be represented using * SigHash and anyoneCanPay See transaction * c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73, which has sigHashType 0 */ public static synchronized byte[] serializeForSignature( Transaction spendingTx, int inputIndex, byte[] connectedScript, byte sigHashType) { NetworkParameters params = TestNet3Params.get(); // The SIGHASH flags are used in the design of contracts, please see this page for a further // understanding of // the purposes of the code in this method: // // https://en.bitcoin.it/wiki/Contracts try { Transaction tx = new Transaction(params, spendingTx.bitcoinSerialize()); // Store all the input scripts and clear them in preparation for signing. If we're signing a // fresh // transaction that step isn't very helpful, but it doesn't add much cost relative to the // actual // EC math so we'll do it anyway. // // Also store the input sequence numbers in case we are clearing them with SigHash.NONE/SINGLE byte[][] inputScripts = new byte[tx.getInputs().size()][]; long[] inputSequenceNumbers = new long[tx.getInputs().size()]; for (int i = 0; i < tx.getInputs().size(); i++) { inputScripts[i] = tx.getInputs().get(i).getScriptBytes(); inputSequenceNumbers[i] = tx.getInputs().get(i).getSequenceNumber(); tx.getInput(i).setScriptSig(new Script(new byte[0])); } // This step has no purpose beyond being synchronized with the reference clients bugs. // OP_CODESEPARATOR // is a legacy holdover from a previous, broken design of executing scripts that shipped in // Bitcoin 0.1. // It was seriously flawed and would have let anyone take anyone elses money. Later versions // switched to // the design we use today where scripts are executed independently but share a stack. This // left the // OP_CODESEPARATOR instruction having no purpose as it was only meant to be used internally, // not actually // ever put into scripts. Deleting OP_CODESEPARATOR is a step that should never be required // but if we don't // do it, we could split off the main chain. connectedScript = Script.removeAllInstancesOfOp(connectedScript, ScriptOpCodes.OP_CODESEPARATOR); // Set the input to the script of its output. Satoshi does this but the step has no obvious // purpose as // the signature covers the hash of the prevout transaction which obviously includes the // output script // already. Perhaps it felt safer to him in some way, or is another leftover from how the code // was written. TransactionInput input = tx.getInputs().get(inputIndex); input.setScriptSig(new Script(connectedScript)); List<TransactionOutput> outputs = tx.getOutputs(); if ((sigHashType & 0x1f) == (Transaction.SigHash.NONE.ordinal() + 1)) { // SIGHASH_NONE means no outputs are signed at all - the signature is effectively for a // "blank cheque". // this.outputs = new ArrayList<TransactionOutput>(0); tx.clearOutputs(); // The signature isn't broken by new versions of the transaction issued by other parties. for (int i = 0; i < tx.getInputs().size(); i++) if (i != inputIndex) tx.getInputs().get(i).setSequenceNumber(0); } else if ((sigHashType & 0x1f) == (Transaction.SigHash.SINGLE.ordinal() + 1)) { // SIGHASH_SINGLE means only sign the output at the same index as the input (ie, my output). if (inputIndex >= tx.getOutputs().size()) { // The input index is beyond the number of outputs, it's a buggy signature made by a // broken // Bitcoin implementation. The reference client also contains a bug in handling this case: // any transaction output that is signed in this case will result in both the signed // output // and any future outputs to this public key being steal-able by anyone who has // the resulting signature and the public key (both of which are part of the signed tx // input). // Put the transaction back to how we found it. // // TODO: Only allow this to happen if we are checking a signature, not signing a // transactions for (int i = 0; i < tx.getInputs().size(); i++) { // tx.getInputs().get(i).setScriptSig(inputScripts[i]); /* tx.getInputs().get(i).setScriptSig(ScriptBuilder.createMultiSigInputScriptBytes( Arrays.asList(inputScripts[i])));*/ tx.getInput(i).setScriptSig(new Script(inputScripts[i])); tx.getInputs().get(i).setSequenceNumber(inputSequenceNumbers[i]); } // this.outputs = outputs; // Satoshis bug is that SignatureHash was supposed to return a hash and on this codepath // it // actually returns the constant "1" to indicate an error, which is never checked for. // Oops. return Utils.HEX.decode( "0100000000000000000000000000000000000000000000000000000000000000"); } // In SIGHASH_SINGLE the outputs after the matching input index are deleted, and the outputs // before // that position are "nulled out". Unintuitively, the value in a "null" transaction is set // to -1. /* this.outputs = new ArrayList<TransactionOutput>(this.outputs.subList(0, inputIndex + 1)); for (int i = 0; i < inputIndex; i++) this.outputs.set(i, new TransactionOutput(params, this, Coin.NEGATIVE_SATOSHI, new byte[] {})); // The signature isn't broken by new versions of the transaction issued by other parties. for (int i = 0; i < inputs.size(); i++) if (i != inputIndex) inputs.get(i).setSequenceNumber(0);*/ // In SIGHASH_SINGLE the outputs after the matching input index are deleted, and the outputs // before // that position are "nulled out". Unintuitively, the value in a "null" transaction is set // to -1. // tx.outputs = new ArrayList<TransactionOutput>(tx.getOutputs().subList(0, inputIndex + // 1)); tx.clearOutputs(); for (int i = 0; i <= inputIndex; i++) if (i == inputIndex) { // need to make sure the output at inputIndex stays the same tx.addOutput(spendingTx.getOutput(inputIndex)); } else { // this.outputs.set(i, new TransactionOutput(params, this, Coin.NEGATIVE_SATOSHI, new // byte[] {})); tx.addOutput(new TransactionOutput(params, tx, Coin.NEGATIVE_SATOSHI, new byte[] {})); } // The signature isn't broken by new versions of the transaction issued by other parties. for (int i = 0; i < tx.getInputs().size(); i++) if (i != inputIndex) tx.getInputs().get(i).setSequenceNumber(0); } List<TransactionInput> inputs = tx.getInputs(); if ((sigHashType & (byte) 0x80) == 0x80) { // SIGHASH_ANYONECANPAY means the signature in the input is not broken by // changes/additions/removals // of other inputs. For example, this is useful for building assurance contracts. tx.clearInputs(); tx.getInputs().add(input); } ByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(256); tx.bitcoinSerialize(bos); // We also have to write a hash type (sigHashType is actually an unsigned char) uint32ToByteStreamLE(0x000000ff & sigHashType, bos); // Note that this is NOT reversed to ensure it will be signed correctly. If it were to be // printed out // however then we would expect that it is IS reversed. byte[] txSignatureBytes = bos.toByteArray(); bos.close(); // Put the transaction back to how we found it. // tx.inputs = inputs; tx.clearInputs(); for (int i = 0; i < inputs.size(); i++) { tx.addInput(inputs.get(i)); } for (int i = 0; i < inputs.size(); i++) { inputs.get(i).setScriptSig(new Script(inputScripts[i])); inputs.get(i).setSequenceNumber(inputSequenceNumbers[i]); } // this.outputs = outputs; tx.clearOutputs(); for (int i = 0; i < outputs.size(); i++) { tx.addOutput(outputs.get(i)); } return txSignatureBytes; } catch (IOException e) { throw new RuntimeException(e); // Cannot happen. } }
private void readTransaction(Protos.Transaction txProto, NetworkParameters params) throws UnreadableWalletException { Transaction tx = new Transaction(params); if (txProto.hasUpdatedAt()) { tx.setUpdateTime(new Date(txProto.getUpdatedAt())); } for (Protos.TransactionOutput outputProto : txProto.getTransactionOutputList()) { Coin value = Coin.valueOf(outputProto.getValue()); byte[] scriptBytes = outputProto.getScriptBytes().toByteArray(); TransactionOutput output = new TransactionOutput(params, tx, value, scriptBytes); tx.addOutput(output); } for (Protos.TransactionInput inputProto : txProto.getTransactionInputList()) { byte[] scriptBytes = inputProto.getScriptBytes().toByteArray(); TransactionOutPoint outpoint = new TransactionOutPoint( params, inputProto.getTransactionOutPointIndex() & 0xFFFFFFFFL, byteStringToHash(inputProto.getTransactionOutPointHash())); Coin value = inputProto.hasValue() ? Coin.valueOf(inputProto.getValue()) : null; TransactionInput input = new TransactionInput(params, tx, scriptBytes, outpoint, value); if (inputProto.hasSequence()) { input.setSequenceNumber(inputProto.getSequence()); } tx.addInput(input); } for (int i = 0; i < txProto.getBlockHashCount(); i++) { ByteString blockHash = txProto.getBlockHash(i); int relativityOffset = 0; if (txProto.getBlockRelativityOffsetsCount() > 0) relativityOffset = txProto.getBlockRelativityOffsets(i); tx.addBlockAppearance(byteStringToHash(blockHash), relativityOffset); } if (txProto.hasLockTime()) { tx.setLockTime(0xffffffffL & txProto.getLockTime()); } if (txProto.hasPurpose()) { switch (txProto.getPurpose()) { case UNKNOWN: tx.setPurpose(Transaction.Purpose.UNKNOWN); break; case USER_PAYMENT: tx.setPurpose(Transaction.Purpose.USER_PAYMENT); break; case KEY_ROTATION: tx.setPurpose(Transaction.Purpose.KEY_ROTATION); break; case ASSURANCE_CONTRACT_CLAIM: tx.setPurpose(Transaction.Purpose.ASSURANCE_CONTRACT_CLAIM); break; case ASSURANCE_CONTRACT_PLEDGE: tx.setPurpose(Transaction.Purpose.ASSURANCE_CONTRACT_PLEDGE); break; case ASSURANCE_CONTRACT_STUB: tx.setPurpose(Transaction.Purpose.ASSURANCE_CONTRACT_STUB); break; default: throw new RuntimeException("New purpose serialization not implemented"); } } else { // Old wallet: assume a user payment as that's the only reason a new tx would have been // created back then. tx.setPurpose(Transaction.Purpose.USER_PAYMENT); } if (txProto.hasExchangeRate()) { Protos.ExchangeRate exchangeRateProto = txProto.getExchangeRate(); tx.setExchangeRate( new ExchangeRate( Coin.valueOf(exchangeRateProto.getCoinValue()), Fiat.valueOf( exchangeRateProto.getFiatCurrencyCode(), exchangeRateProto.getFiatValue()))); } if (txProto.hasMemo()) tx.setMemo(txProto.getMemo()); // Transaction should now be complete. Sha256Hash protoHash = byteStringToHash(txProto.getHash()); if (!tx.getHash().equals(protoHash)) throw new UnreadableWalletException( String.format( "Transaction did not deserialize completely: %s vs %s", tx.getHash(), protoHash)); if (txMap.containsKey(txProto.getHash())) throw new UnreadableWalletException( "Wallet contained duplicate transaction " + byteStringToHash(txProto.getHash())); txMap.put(txProto.getHash(), tx); }
private static Protos.Transaction makeTxProto(WalletTransaction wtx) { Transaction tx = wtx.getTransaction(); Protos.Transaction.Builder txBuilder = Protos.Transaction.newBuilder(); txBuilder .setPool(getProtoPool(wtx)) .setHash(hashToByteString(tx.getHash())) .setVersion((int) tx.getVersion()); if (tx.getUpdateTime() != null) { txBuilder.setUpdatedAt(tx.getUpdateTime().getTime()); } if (tx.getLockTime() > 0) { txBuilder.setLockTime((int) tx.getLockTime()); } // Handle inputs. for (TransactionInput input : tx.getInputs()) { Protos.TransactionInput.Builder inputBuilder = Protos.TransactionInput.newBuilder() .setScriptBytes(ByteString.copyFrom(input.getScriptBytes())) .setTransactionOutPointHash(hashToByteString(input.getOutpoint().getHash())) .setTransactionOutPointIndex((int) input.getOutpoint().getIndex()); if (input.hasSequence()) inputBuilder.setSequence((int) input.getSequenceNumber()); if (input.getValue() != null) inputBuilder.setValue(input.getValue().value); txBuilder.addTransactionInput(inputBuilder); } // Handle outputs. for (TransactionOutput output : tx.getOutputs()) { Protos.TransactionOutput.Builder outputBuilder = Protos.TransactionOutput.newBuilder() .setScriptBytes(ByteString.copyFrom(output.getScriptBytes())) .setValue(output.getValue().value); final TransactionInput spentBy = output.getSpentBy(); if (spentBy != null) { Sha256Hash spendingHash = spentBy.getParentTransaction().getHash(); int spentByTransactionIndex = spentBy.getParentTransaction().getInputs().indexOf(spentBy); outputBuilder .setSpentByTransactionHash(hashToByteString(spendingHash)) .setSpentByTransactionIndex(spentByTransactionIndex); } txBuilder.addTransactionOutput(outputBuilder); } // Handle which blocks tx was seen in. final Map<Sha256Hash, Integer> appearsInHashes = tx.getAppearsInHashes(); if (appearsInHashes != null) { for (Map.Entry<Sha256Hash, Integer> entry : appearsInHashes.entrySet()) { txBuilder.addBlockHash(hashToByteString(entry.getKey())); txBuilder.addBlockRelativityOffsets(entry.getValue()); } } if (tx.hasConfidence()) { TransactionConfidence confidence = tx.getConfidence(); Protos.TransactionConfidence.Builder confidenceBuilder = Protos.TransactionConfidence.newBuilder(); writeConfidence(txBuilder, confidence, confidenceBuilder); } Protos.Transaction.Purpose purpose; switch (tx.getPurpose()) { case UNKNOWN: purpose = Protos.Transaction.Purpose.UNKNOWN; break; case USER_PAYMENT: purpose = Protos.Transaction.Purpose.USER_PAYMENT; break; case KEY_ROTATION: purpose = Protos.Transaction.Purpose.KEY_ROTATION; break; case ASSURANCE_CONTRACT_CLAIM: purpose = Protos.Transaction.Purpose.ASSURANCE_CONTRACT_CLAIM; break; case ASSURANCE_CONTRACT_PLEDGE: purpose = Protos.Transaction.Purpose.ASSURANCE_CONTRACT_PLEDGE; break; case ASSURANCE_CONTRACT_STUB: purpose = Protos.Transaction.Purpose.ASSURANCE_CONTRACT_STUB; break; default: throw new RuntimeException("New tx purpose serialization not implemented."); } txBuilder.setPurpose(purpose); ExchangeRate exchangeRate = tx.getExchangeRate(); if (exchangeRate != null) { Protos.ExchangeRate.Builder exchangeRateBuilder = Protos.ExchangeRate.newBuilder() .setCoinValue(exchangeRate.coin.value) .setFiatValue(exchangeRate.fiat.value) .setFiatCurrencyCode(exchangeRate.fiat.currencyCode); txBuilder.setExchangeRate(exchangeRateBuilder); } if (tx.getMemo() != null) txBuilder.setMemo(tx.getMemo()); return txBuilder.build(); }
@Override /** Used during reorgs to connect a block previously on a fork */ protected synchronized TransactionOutputChanges connectTransactions(StoredBlock newBlock) throws VerificationException, BlockStoreException, PrunedException { checkState(lock.isHeldByCurrentThread()); if (!params.passesCheckpoint(newBlock.getHeight(), newBlock.getHeader().getHash())) throw new VerificationException("Block failed checkpoint lockin at " + newBlock.getHeight()); blockStore.beginDatabaseBatchWrite(); StoredUndoableBlock block = blockStore.getUndoBlock(newBlock.getHeader().getHash()); if (block == null) { // We're trying to re-org too deep and the data needed has been deleted. blockStore.abortDatabaseBatchWrite(); throw new PrunedException(newBlock.getHeader().getHash()); } TransactionOutputChanges txOutChanges; try { List<Transaction> transactions = block.getTransactions(); if (transactions != null) { LinkedList<UTXO> txOutsSpent = new LinkedList<UTXO>(); LinkedList<UTXO> txOutsCreated = new LinkedList<UTXO>(); long sigOps = 0; final Set<VerifyFlag> verifyFlags = EnumSet.noneOf(VerifyFlag.class); if (newBlock.getHeader().getTimeSeconds() >= NetworkParameters.BIP16_ENFORCE_TIME) verifyFlags.add(VerifyFlag.P2SH); if (!params.isCheckpoint(newBlock.getHeight())) { for (Transaction tx : transactions) { Sha256Hash hash = tx.getHash(); if (blockStore.hasUnspentOutputs(hash, tx.getOutputs().size())) throw new VerificationException("Block failed BIP30 test!"); } } Coin totalFees = Coin.ZERO; Coin coinbaseValue = null; if (scriptVerificationExecutor.isShutdown()) scriptVerificationExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); List<Future<VerificationException>> listScriptVerificationResults = new ArrayList<Future<VerificationException>>(transactions.size()); for (final Transaction tx : transactions) { boolean isCoinBase = tx.isCoinBase(); Coin valueIn = Coin.ZERO; Coin valueOut = Coin.ZERO; final List<Script> prevOutScripts = new LinkedList<Script>(); if (!isCoinBase) { for (int index = 0; index < tx.getInputs().size(); index++) { final TransactionInput in = tx.getInputs().get(index); final UTXO prevOut = blockStore.getTransactionOutput( in.getOutpoint().getHash(), in.getOutpoint().getIndex()); if (prevOut == null) throw new VerificationException( "Attempted spend of a non-existent or already spent output!"); if (prevOut.isCoinbase() && newBlock.getHeight() - prevOut.getHeight() < params.getSpendableCoinbaseDepth()) throw new VerificationException( "Tried to spend coinbase at depth " + (newBlock.getHeight() - prevOut.getHeight())); valueIn = valueIn.add(prevOut.getValue()); if (verifyFlags.contains(VerifyFlag.P2SH)) { if (prevOut.getScript().isPayToScriptHash()) sigOps += Script.getP2SHSigOpCount(in.getScriptBytes()); if (sigOps > Block.MAX_BLOCK_SIGOPS) throw new VerificationException("Too many P2SH SigOps in block"); } prevOutScripts.add(prevOut.getScript()); blockStore.removeUnspentTransactionOutput(prevOut); txOutsSpent.add(prevOut); } } Sha256Hash hash = tx.getHash(); for (TransactionOutput out : tx.getOutputs()) { valueOut = valueOut.add(out.getValue()); Script script = getScript(out.getScriptBytes()); UTXO newOut = new UTXO( hash, out.getIndex(), out.getValue(), newBlock.getHeight(), isCoinBase, script, getScriptAddress(script)); 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.signum() < 0 || valueOut.compareTo(params.getMaxMoney()) > 0) throw new VerificationException("Transaction output value out of range"); if (isCoinBase) { coinbaseValue = valueOut; } else { if (valueIn.compareTo(valueOut) < 0 || valueIn.compareTo(params.getMaxMoney()) > 0) throw new VerificationException("Transaction input value out of range"); totalFees = totalFees.add(valueIn.subtract(valueOut)); } if (!isCoinBase) { // Because correctlySpends modifies transactions, this must come after we are done with // tx FutureTask<VerificationException> future = new FutureTask<VerificationException>( new Verifier(tx, prevOutScripts, verifyFlags)); scriptVerificationExecutor.execute(future); listScriptVerificationResults.add(future); } } if (totalFees.compareTo(params.getMaxMoney()) > 0 || newBlock .getHeader() .getBlockInflation(newBlock.getHeight()) .add(totalFees) .compareTo(coinbaseValue) < 0) throw new VerificationException("Transaction fees out of range"); txOutChanges = new TransactionOutputChanges(txOutsCreated, txOutsSpent); 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.", thrownE); } if (e != null) throw e; } } else { txOutChanges = block.getTxOutChanges(); if (!params.isCheckpoint(newBlock.getHeight())) for (UTXO out : txOutChanges.txOutsCreated) { Sha256Hash hash = out.getHash(); if (blockStore.getTransactionOutput(hash, out.getIndex()) != null) throw new VerificationException("Block failed BIP30 test!"); } for (UTXO out : txOutChanges.txOutsCreated) blockStore.addUnspentTransactionOutput(out); for (UTXO out : txOutChanges.txOutsSpent) blockStore.removeUnspentTransactionOutput(out); } } catch (VerificationException e) { scriptVerificationExecutor.shutdownNow(); blockStore.abortDatabaseBatchWrite(); throw e; } catch (BlockStoreException e) { scriptVerificationExecutor.shutdownNow(); blockStore.abortDatabaseBatchWrite(); throw e; } return txOutChanges; }
@Override protected TransactionOutputChanges connectTransactions(int height, Block block) throws VerificationException, BlockStoreException { checkState(lock.isHeldByCurrentThread()); 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<UTXO> txOutsSpent = new LinkedList<UTXO>(); LinkedList<UTXO> txOutsCreated = new LinkedList<UTXO>(); long sigOps = 0; final Set<VerifyFlag> verifyFlags = EnumSet.noneOf(VerifyFlag.class); if (block.getTimeSeconds() >= NetworkParameters.BIP16_ENFORCE_TIME) verifyFlags.add(VerifyFlag.P2SH); 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://github.com/bitcoin/bips/blob/master/bip-0030.mediawiki 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 (verifyFlags.contains( VerifyFlag .P2SH)) // We already check non-BIP16 sigops in Block.verifyTransactions(true) sigOps += tx.getSigOpCount(); } } Coin totalFees = Coin.ZERO; Coin coinbaseValue = null; for (final Transaction tx : block.transactions) { boolean isCoinBase = tx.isCoinBase(); Coin valueIn = Coin.ZERO; Coin valueOut = Coin.ZERO; final List<Script> prevOutScripts = new LinkedList<Script>(); 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); UTXO 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 (prevOut.isCoinbase()) { 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 (verifyFlags.contains(VerifyFlag.P2SH)) { if (prevOut.getScript().isPayToScriptHash()) sigOps += Script.getP2SHSigOpCount(in.getScriptBytes()); if (sigOps > Block.MAX_BLOCK_SIGOPS) throw new VerificationException("Too many P2SH SigOps in block"); } prevOutScripts.add(prevOut.getScript()); 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. Script script = getScript(out.getScriptBytes()); UTXO newOut = new UTXO( hash, out.getIndex(), out.getValue(), height, isCoinBase, script, getScriptAddress(script)); 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.signum() < 0 || valueOut.compareTo(params.getMaxMoney()) > 0) throw new VerificationException("Transaction output value out of range"); if (isCoinBase) { coinbaseValue = valueOut; } else { if (valueIn.compareTo(valueOut) < 0 || valueIn.compareTo(params.getMaxMoney()) > 0) throw new VerificationException("Transaction input value out of range"); totalFees = totalFees.add(valueIn.subtract(valueOut)); } if (!isCoinBase && runScripts) { // Because correctlySpends modifies transactions, this must come after we are done with tx FutureTask<VerificationException> future = new FutureTask<VerificationException>(new Verifier(tx, prevOutScripts, verifyFlags)); scriptVerificationExecutor.execute(future); listScriptVerificationResults.add(future); } } if (totalFees.compareTo(params.getMaxMoney()) > 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.", thrownE); } 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); }