/** * Called when the client provides the refund transaction. The refund transaction must have one * input from the multisig contract (that we don't have yet) and one output that the client * creates to themselves. This object will later be modified when we start getting paid. * * @param refundTx The refund transaction, this object will be mutated when payment is * incremented. * @param clientMultiSigPubKey The client's pubkey which is required for the multisig output * @return Our signature that makes the refund transaction valid * @throws VerificationException If the transaction isnt valid or did not meet the requirements of * a refund transaction. */ public synchronized byte[] provideRefundTransaction( Transaction refundTx, byte[] clientMultiSigPubKey) throws VerificationException { checkNotNull(refundTx); checkNotNull(clientMultiSigPubKey); checkState(state == State.WAITING_FOR_REFUND_TRANSACTION); log.info("Provided with refund transaction: {}", refundTx); // Do a few very basic syntax sanity checks. refundTx.verify(); // Verify that the refund transaction has a single input (that we can fill to sign the multisig // output). if (refundTx.getInputs().size() != 1) throw new VerificationException("Refund transaction does not have exactly one input"); // Verify that the refund transaction has a time lock on it and a sequence number of zero. if (refundTx.getInput(0).getSequenceNumber() != 0) throw new VerificationException("Refund transaction's input's sequence number is non-0"); if (refundTx.getLockTime() < minExpireTime) throw new VerificationException("Refund transaction has a lock time too soon"); // Verify the transaction has one output (we don't care about its contents, its up to the // client) // Note that because we sign with SIGHASH_NONE|SIGHASH_ANYOENCANPAY the client can later add // more outputs and // inputs, but we will need only one output later to create the paying transactions if (refundTx.getOutputs().size() != 1) throw new VerificationException("Refund transaction does not have exactly one output"); refundTransactionUnlockTimeSecs = refundTx.getLockTime(); // Sign the refund tx with the scriptPubKey and return the signature. We don't have the spending // transaction // so do the steps individually. clientKey = ECKey.fromPublicOnly(clientMultiSigPubKey); Script multisigPubKey = ScriptBuilder.createMultiSigOutputScript(2, ImmutableList.of(clientKey, serverKey)); // We are really only signing the fact that the transaction has a proper lock time and don't // care about anything // else, so we sign SIGHASH_NONE and SIGHASH_ANYONECANPAY. TransactionSignature sig = refundTx.calculateSignature(0, serverKey, multisigPubKey, Transaction.SigHash.NONE, true); log.info("Signed refund transaction."); this.clientOutput = refundTx.getOutput(0); state = State.WAITING_FOR_MULTISIG_CONTRACT; return sig.encodeToBitcoin(); }
PaymentChannelServerState( StoredServerChannel storedServerChannel, Wallet wallet, TransactionBroadcaster broadcaster) throws VerificationException { synchronized (storedServerChannel) { this.wallet = checkNotNull(wallet); this.broadcaster = checkNotNull(broadcaster); this.multisigContract = checkNotNull(storedServerChannel.contract); this.multisigScript = multisigContract.getOutput(0).getScriptPubKey(); this.clientKey = ECKey.fromPublicOnly(multisigScript.getChunks().get(1).data); this.clientOutput = checkNotNull(storedServerChannel.clientOutput); this.refundTransactionUnlockTimeSecs = storedServerChannel.refundTransactionUnlockTimeSecs; this.serverKey = checkNotNull(storedServerChannel.myKey); this.totalValue = multisigContract.getOutput(0).getValue(); this.bestValueToMe = checkNotNull(storedServerChannel.bestValueToMe); this.bestValueSignature = storedServerChannel.bestValueSignature; checkArgument(bestValueToMe.equals(Coin.ZERO) || bestValueSignature != null); this.storedServerChannel = storedServerChannel; storedServerChannel.state = this; this.state = State.READY; } }