/** * When the servers signature for the refund transaction is received, call this to verify it and * sign the complete refund ourselves. * * <p>If this does not throw an exception, we are secure against the loss of funds and can safely * provide the server with the multi-sig contract to lock in the agreement. In this case, both the * multisig contract and the refund transaction are automatically committed to wallet so that it * can handle broadcasting the refund transaction at the appropriate time if necessary. */ public synchronized void provideRefundSignature(byte[] theirSignature) throws VerificationException { checkNotNull(theirSignature); checkState(state == State.WAITING_FOR_SIGNED_REFUND); TransactionSignature theirSig = TransactionSignature.decodeFromBitcoin(theirSignature, true); if (theirSig.sigHashMode() != Transaction.SigHash.NONE || !theirSig.anyoneCanPay()) throw new VerificationException("Refund signature was not SIGHASH_NONE|SIGHASH_ANYONECANPAY"); // Sign the refund transaction ourselves. final TransactionOutput multisigContractOutput = multisigContract.getOutput(0); try { multisigScript = multisigContractOutput.getScriptPubKey(); } catch (ScriptException e) { throw new RuntimeException(e); // Cannot happen: we built this ourselves. } TransactionSignature ourSignature = refundTx.calculateSignature(0, myKey, multisigScript, Transaction.SigHash.ALL, false); // Insert the signatures. Script scriptSig = ScriptBuilder.createMultiSigInputScript(ourSignature, theirSig); log.info("Refund scriptSig: {}", scriptSig); log.info("Multi-sig contract scriptPubKey: {}", multisigScript); TransactionInput refundInput = refundTx.getInput(0); refundInput.setScriptSig(scriptSig); refundInput.verify(multisigContractOutput); state = State.SAVE_STATE_IN_WALLET; }
/** Returns true if the tx is a valid settlement transaction. */ public synchronized boolean isSettlementTransaction(Transaction tx) { try { tx.verify(); tx.getInput(0).verify(multisigContract.getOutput(0)); return true; } catch (VerificationException e) { return false; } }
private WalletTransaction connectTransactionOutputs( org.bitcoinj.wallet.Protos.Transaction txProto) { Transaction tx = txMap.get(txProto.getHash()); WalletTransaction.Pool pool = WalletTransaction.Pool.valueOf(txProto.getPool().getNumber()); if (pool == WalletTransaction.Pool.INACTIVE || pool == WalletTransaction.Pool.PENDING_INACTIVE) { // 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. pool = WalletTransaction.Pool.PENDING; } 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 IllegalArgumentException( 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); }