private static void sendTransactionsToListener( StoredBlock block, NewBlockType blockType, BlockChainListener listener, int relativityOffset, List<Transaction> transactions, boolean clone, Set<Sha256Hash> falsePositives) throws VerificationException { for (Transaction tx : transactions) { try { if (listener.isTransactionRelevant(tx)) { falsePositives.remove(tx.getHash()); if (clone) tx = new Transaction(tx.params, tx.bitcoinSerialize()); listener.receiveFromBlock(tx, block, blockType, relativityOffset++); } } catch (ScriptException e) { // We don't want scripts we don't understand to break the block chain so just note that this // tx was // not scanned here and continue. log.warn("Failed to parse a script: " + e.toString()); } catch (ProtocolException e) { // Failed to duplicate tx, should never happen. throw new RuntimeException(e); } } }
@VisibleForTesting synchronized void doStoreChannelInWallet(Sha256Hash id) { StoredPaymentChannelClientStates channels = (StoredPaymentChannelClientStates) wallet.getExtensions().get(StoredPaymentChannelClientStates.EXTENSION_ID); checkNotNull( channels, "You have not added the StoredPaymentChannelClientStates extension to the wallet."); checkState(channels.getChannel(id, multisigContract.getHash()) == null); storedChannel = new StoredClientChannel(id, multisigContract, refundTx, myKey, valueToMe, refundFees, true); channels.putChannel(storedChannel); wallet.addOrUpdateExtension(channels); }
/** * Stores this channel's state in the wallet as a part of a {@link * StoredPaymentChannelServerStates} wallet extension and keeps it up-to-date each time payment is * incremented. This will be automatically removed when a call to {@link * PaymentChannelServerState#close()} completes successfully. A channel may only be stored after * it has fully opened (ie state == State.READY). * * @param connectedHandler Optional {@link PaymentChannelServer} object that manages this object. * This will set the appropriate pointer in the newly created {@link StoredServerChannel} * before it is committed to wallet. If set, closing the state object will propagate the close * to the handler which can then do a TCP disconnect. */ public synchronized void storeChannelInWallet(@Nullable PaymentChannelServer connectedHandler) { checkState(state == State.READY); if (storedServerChannel != null) return; log.info("Storing state with contract hash {}.", multisigContract.getHash()); StoredPaymentChannelServerStates channels = (StoredPaymentChannelServerStates) wallet.addOrGetExistingExtension( new StoredPaymentChannelServerStates(wallet, broadcaster)); storedServerChannel = new StoredServerChannel( this, multisigContract, clientOutput, refundTransactionUnlockTimeSecs, serverKey, bestValueToMe, bestValueSignature); if (connectedHandler != null) checkState( storedServerChannel.setConnectedHandler(connectedHandler, false) == connectedHandler); channels.putChannel(storedServerChannel); }
/** * Called when the client provides us with a new signature and wishes to increment total payment * by size. Verifies the provided signature and only updates values if everything checks out. If * the new refundSize is not the lowest we have seen, it is simply ignored. * * @param refundSize How many satoshis of the original contract are refunded to the client (the * rest are ours) * @param signatureBytes The new signature spending the multi-sig contract to a new payment * transaction * @throws VerificationException If the signature does not verify or size is out of range (incl * being rejected by the network as dust). * @return true if there is more value left on the channel, false if it is now fully used up. */ public synchronized boolean incrementPayment(Coin refundSize, byte[] signatureBytes) throws VerificationException, ValueOutOfRangeException, InsufficientMoneyException { checkState(state == State.READY); checkNotNull(refundSize); checkNotNull(signatureBytes); TransactionSignature signature = TransactionSignature.decodeFromBitcoin(signatureBytes, true); // We allow snapping to zero for the payment amount because it's treated specially later, but // not less than // the dust level because that would prevent the transaction from being relayed/mined. final boolean fullyUsedUp = refundSize.equals(Coin.ZERO); if (refundSize.compareTo(clientOutput.getMinNonDustValue()) < 0 && !fullyUsedUp) throw new ValueOutOfRangeException( "Attempt to refund negative value or value too small to be accepted by the network"); Coin newValueToMe = totalValue.subtract(refundSize); if (newValueToMe.signum() < 0) throw new ValueOutOfRangeException("Attempt to refund more than the contract allows."); if (newValueToMe.compareTo(bestValueToMe) < 0) throw new ValueOutOfRangeException("Attempt to roll back payment on the channel."); // Get the wallet's copy of the multisigContract (ie with confidence information), if this is // null, the wallet // was not connected to the peergroup when the contract was broadcast (which may cause issues // down the road, and // disables our double-spend check next) Transaction walletContract = wallet.getTransaction(multisigContract.getHash()); checkNotNull( walletContract, "Wallet did not contain multisig contract {} after state was marked READY", multisigContract.getHash()); // Note that we check for DEAD state here, but this test is essentially useless in production // because we will // miss most double-spends due to bloom filtering right now anyway. This will eventually fixed // by network-wide // double-spend notifications, so we just wait instead of attempting to add all dependant // outpoints to our bloom // filters (and probably missing lots of edge-cases). if (walletContract.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.DEAD) { close(); throw new VerificationException("Multisig contract was double-spent"); } Transaction.SigHash mode; // If the client doesn't want anything back, they shouldn't sign any outputs at all. if (fullyUsedUp) mode = Transaction.SigHash.NONE; else mode = Transaction.SigHash.SINGLE; if (signature.sigHashMode() != mode || !signature.anyoneCanPay()) throw new VerificationException( "New payment signature was not signed with the right SIGHASH flags."); Wallet.SendRequest req = makeUnsignedChannelContract(newValueToMe); // Now check the signature is correct. // Note that the client must sign with SIGHASH_{SINGLE/NONE} | SIGHASH_ANYONECANPAY to allow us // to add additional // inputs (in case we need to add significant fee, or something...) and any outputs we want to // pay to. Sha256Hash sighash = req.tx.hashForSignature(0, multisigScript, mode, true); if (!clientKey.verify(sighash, signature)) throw new VerificationException("Signature does not verify on tx\n" + req.tx); bestValueToMe = newValueToMe; bestValueSignature = signatureBytes; updateChannelInWallet(); return !fullyUsedUp; }