示例#1
0
  public static List<TransactionOutput> getMyOutputs(Transaction tx, DeterministicKey key) {
    List<TransactionOutput> mines = new ArrayList<>();
    for (TransactionOutput curr : tx.getOutputs()) {
      boolean isMine = false;

      String to = null;
      Address add = curr.getAddressFromP2PKHScript(BitcoinNetwork.getInstance().get().getParams());
      if (add != null) to = add.toString();
      else {
        add = curr.getAddressFromP2SH(BitcoinNetwork.getInstance().get().getParams());
        if (add != null) to = add.toString();
      }
      if (to != null) { // VERIFICATION BY ADDRESS
        isMine = to.equals(keyToStringAddress(key));
      } else { // VERIFICATION BY PUBKEY
        final byte[] pubKeyCurr = curr.getScriptPubKey().getPubKey();
        isMine = Arrays.equals(key.getPubKey(), pubKeyCurr);
      }

      if (isMine) {
        mines.add(curr);
        break;
      }
    }
    return mines;
  }
 @Override
 public CoinSelection select(Coin target, List<TransactionOutput> candidates) {
   ArrayList<TransactionOutput> selected = new ArrayList<TransactionOutput>();
   // Sort the inputs by age*value so we get the highest "coindays" spent.
   // TODO: Consider changing the wallets internal format to track just outputs and keep them
   // ordered.
   ArrayList<TransactionOutput> sortedOutputs = new ArrayList<TransactionOutput>(candidates);
   // When calculating the wallet balance, we may be asked to select all possible coins, if so,
   // avoid sorting
   // them in order to improve performance.
   // TODO: Take in network parameters when instanatiated, and then test against the current
   // network. Or just have a boolean parameter for "give me everything"
   if (!target.equals(NetworkParameters.MAX_MONEY)) {
     sortOutputs(sortedOutputs);
   }
   // Now iterate over the sorted outputs until we have got as close to the target as possible or a
   // little
   // bit over (excessive value will be change).
   long total = 0;
   for (TransactionOutput output : sortedOutputs) {
     if (total >= target.value) break;
     // Only pick chain-included transactions, or transactions that are ours and pending.
     if (!shouldSelect(output.getParentTransaction())) continue;
     selected.add(output);
     total += output.getValue().value;
   }
   // Total may be lower than target here, if the given candidates were insufficient to create to
   // requested
   // transaction.
   return new CoinSelection(Coin.valueOf(total), selected);
 }
 @CheckForNull
 private static List<Address> getToAddresses(
     @Nonnull final Transaction tx, @Nonnull final AbstractWallet pocket, boolean toMe) {
   List<Address> addresses = new ArrayList<Address>();
   for (final TransactionOutput output : tx.getOutputs()) {
     try {
       if (output.isMine(pocket) == toMe) {
         addresses.add(output.getScriptPubKey().getToAddress(pocket.getCoinType()));
       }
     } catch (final ScriptException x) {
       /* ignore this output */
     }
   }
   return addresses;
 }
  public static boolean isInternal(@Nonnull final Transaction tx) {
    if (tx.isCoinBase()) return false;

    final List<TransactionOutput> outputs = tx.getOutputs();
    if (outputs.size() != 1) return false;

    try {
      final TransactionOutput output = outputs.get(0);
      final Script scriptPubKey = output.getScriptPubKey();
      if (!scriptPubKey.isSentToRawPubKey()) return false;

      return true;
    } catch (final ScriptException x) {
      return false;
    }
  }
 // Create a payment transaction with valueToMe going back to us
 private synchronized Wallet.SendRequest makeUnsignedChannelContract(Coin valueToMe) {
   Transaction tx = new Transaction(wallet.getParams());
   if (!totalValue.subtract(valueToMe).equals(Coin.ZERO)) {
     clientOutput.setValue(totalValue.subtract(valueToMe));
     tx.addOutput(clientOutput);
   }
   tx.addInput(multisigContract.getOutput(0));
   return Wallet.SendRequest.forTx(tx);
 }
  private void tryOpenDispute(boolean isSupportTicket) {
    if (trade != null) {
      Transaction depositTx = trade.getDepositTx();
      if (depositTx != null) {
        doOpenDispute(isSupportTicket, trade.getDepositTx());
      } else {
        log.warn("Trade.depositTx is null. We try to find the tx in our wallet.");
        List<Transaction> candidates = new ArrayList<>();
        List<Transaction> transactions = walletService.getWallet().getRecentTransactions(100, true);
        transactions
            .stream()
            .forEach(
                transaction -> {
                  Coin valueSentFromMe = transaction.getValueSentFromMe(walletService.getWallet());
                  if (!valueSentFromMe.isZero()) {
                    // spending tx
                    for (TransactionOutput transactionOutput : transaction.getOutputs()) {
                      if (!transactionOutput.isMine(walletService.getWallet())) {
                        if (transactionOutput.getScriptPubKey().isPayToScriptHash()) {
                          // MS tx
                          candidates.add(transaction);
                        }
                      }
                    }
                  }
                });

        if (candidates.size() == 1) doOpenDispute(isSupportTicket, candidates.get(0));
        else if (candidates.size() > 1)
          new SelectDepositTxPopup()
              .transactions(candidates)
              .onSelect(
                  transaction -> {
                    doOpenDispute(isSupportTicket, transaction);
                  })
              .closeButtonText("Cancel")
              .show();
        else log.error("Trade.depositTx is null and we did not find any MultiSig transaction.");
      }
    } else {
      log.error("Trade is null");
    }
  }
  /**
   * 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;
  }
  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();
  }