Exemplo n.º 1
0
 @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);
 }
Exemplo n.º 2
0
 private void parsePaymentRequest(Protos.PaymentRequest request) throws PaymentProtocolException {
   try {
     if (request == null) throw new PaymentProtocolException("request cannot be null");
     if (request.getPaymentDetailsVersion() != 1)
       throw new PaymentProtocolException.InvalidVersion(
           "Version 1 required. Received version " + request.getPaymentDetailsVersion());
     paymentRequest = request;
     if (!request.hasSerializedPaymentDetails())
       throw new PaymentProtocolException("No PaymentDetails");
     paymentDetails =
         Protos.PaymentDetails.newBuilder()
             .mergeFrom(request.getSerializedPaymentDetails())
             .build();
     if (paymentDetails == null) throw new PaymentProtocolException("Invalid PaymentDetails");
     if (!paymentDetails.hasNetwork()) params = MainNetParams.get();
     else params = NetworkParameters.fromPmtProtocolID(paymentDetails.getNetwork());
     if (params == null)
       throw new PaymentProtocolException.InvalidNetwork(
           "Invalid network " + paymentDetails.getNetwork());
     if (paymentDetails.getOutputsCount() < 1)
       throw new PaymentProtocolException.InvalidOutputs("No outputs");
     for (Protos.Output output : paymentDetails.getOutputsList()) {
       if (output.hasAmount()) totalValue = totalValue.add(Coin.valueOf(output.getAmount()));
     }
     // This won't ever happen in practice. It would only happen if the user provided outputs
     // that are obviously invalid. Still, we don't want to silently overflow.
     if (params.hasMaxMoney() && totalValue.compareTo(params.getMaxMoney()) > 0)
       throw new PaymentProtocolException.InvalidOutputs("The outputs are way too big.");
   } catch (InvalidProtocolBufferException e) {
     throw new PaymentProtocolException(e);
   }
 }
  private void addContent() {
    addMultilineLabel(
        gridPane,
        ++rowIndex,
        "Please use that only in emergency case if you cannot access your fund from the UI.\n"
            + "Before you use this tool, you should backup your data directory. After you have successfully transferred your wallet balance, remove"
            + " the db directory inside the data directory to start with a newly created and consistent data structure.\n"
            + "Please make a bug report on Github so that we can investigate what was causing the problem.",
        10);

    Coin totalBalance = walletService.getAvailableBalance();
    boolean isBalanceSufficient = totalBalance.compareTo(FeePolicy.TX_FEE) >= 0;
    addressTextField =
        addLabelTextField(
                gridPane,
                ++rowIndex,
                "Your available wallet balance:",
                formatter.formatCoinWithCode(totalBalance),
                10)
            .second;
    Tuple2<Label, InputTextField> tuple =
        addLabelInputTextField(gridPane, ++rowIndex, "Your destination address:");
    addressInputTextField = tuple.second;

    emptyWalletButton = new Button("Empty wallet");
    emptyWalletButton.setDefaultButton(isBalanceSufficient);
    emptyWalletButton.setDisable(
        !isBalanceSufficient && addressInputTextField.getText().length() > 0);
    emptyWalletButton.setOnAction(
        e -> {
          if (addressInputTextField.getText().length() > 0 && isBalanceSufficient) {
            if (walletService.getWallet().isEncrypted()) {
              walletPasswordPopup
                  .onClose(() -> blurAgain())
                  .onAesKey(aesKey -> doEmptyWallet(aesKey))
                  .show();
            } else {
              doEmptyWallet(null);
            }
          }
        });

    closeButton = new Button("Cancel");
    closeButton.setOnAction(
        e -> {
          hide();
          closeHandlerOptional.ifPresent(closeHandler -> closeHandler.run());
        });

    HBox hBox = new HBox();
    hBox.setSpacing(10);
    GridPane.setRowIndex(hBox, ++rowIndex);
    GridPane.setColumnIndex(hBox, 1);
    hBox.getChildren().addAll(emptyWalletButton, closeButton);
    gridPane.getChildren().add(hBox);
    GridPane.setMargin(hBox, new Insets(10, 0, 0, 0));
  }
 // 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);
 }
  @Test
  public void testGetOpenTransactionOutputs() throws Exception {
    final int UNDOABLE_BLOCKS_STORED = 10;
    store = createStore(params, UNDOABLE_BLOCKS_STORED);
    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);
    Transaction transaction = rollingBlock.getTransactions().get(0);
    TransactionOutPoint spendableOutput = new TransactionOutPoint(params, 0, transaction.getHash());
    byte[] spendableOutputScriptPubKey = transaction.getOutputs().get(0).getScriptBytes();
    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);

    // Create bitcoin spend of 1 BTC.
    ECKey toKey = new ECKey();
    Coin amount = Coin.valueOf(100000000);
    Address address = new Address(params, toKey.getPubKeyHash());
    Coin totalAmount = Coin.ZERO;

    Transaction t = new Transaction(params);
    t.addOutput(new TransactionOutput(params, t, amount, toKey));
    t.addSignedInput(spendableOutput, new Script(spendableOutputScriptPubKey), outKey);
    rollingBlock.addTransaction(t);
    rollingBlock.solve();
    chain.add(rollingBlock);
    totalAmount = totalAmount.add(amount);

    List<UTXO> outputs = store.getOpenTransactionOutputs(Lists.newArrayList(address));
    assertNotNull(outputs);
    assertEquals("Wrong Number of Outputs", 1, outputs.size());
    UTXO output = outputs.get(0);
    assertEquals("The address is not equal", address.toString(), output.getAddress());
    assertEquals("The amount is not equal", totalAmount, output.getValue());

    outputs = null;
    output = null;
    try {
      store.close();
    } catch (Exception e) {
    }
  }
Exemplo n.º 6
0
  /**
   * @param params The network parameters or null
   * @param nameValuePairTokens The tokens representing the name value pairs (assumed to be
   *     separated by '=' e.g. 'amount=0.2')
   */
  private void parseParameters(
      @Nullable NetworkParameters params, String addressToken, String[] nameValuePairTokens)
      throws BitcoinURIParseException {
    // Attempt to decode the rest of the tokens into a parameter map.
    for (String nameValuePairToken : nameValuePairTokens) {
      final int sepIndex = nameValuePairToken.indexOf('=');
      if (sepIndex == -1)
        throw new BitcoinURIParseException(
            "Malformed Bitcoin URI - no separator in '" + nameValuePairToken + "'");
      if (sepIndex == 0)
        throw new BitcoinURIParseException(
            "Malformed Bitcoin URI - empty name '" + nameValuePairToken + "'");
      final String nameToken =
          nameValuePairToken.substring(0, sepIndex).toLowerCase(Locale.ENGLISH);
      final String valueToken = nameValuePairToken.substring(sepIndex + 1);

      // Parse the amount.
      if (FIELD_AMOUNT.equals(nameToken)) {
        // Decode the amount (contains an optional decimal component to 8dp).
        try {
          Coin amount = Coin.parseCoin(valueToken);
          if (params != null && amount.isGreaterThan(params.getMaxMoney()))
            throw new BitcoinURIParseException("Max number of coins exceeded");
          if (amount.signum() < 0) throw new ArithmeticException("Negative coins specified");
          putWithValidation(FIELD_AMOUNT, amount);
        } catch (IllegalArgumentException e) {
          throw new OptionalFieldValidationException(
              String.format(Locale.US, "'%s' is not a valid amount", valueToken), e);
        } catch (ArithmeticException e) {
          throw new OptionalFieldValidationException(
              String.format(Locale.US, "'%s' has too many decimal places", valueToken), e);
        }
      } else {
        if (nameToken.startsWith("req-")) {
          // A required parameter that we do not know about.
          throw new RequiredFieldValidationException(
              "'" + nameToken + "' is required but not known, this URI is not valid");
        } else {
          // Known fields and unknown parameters that are optional.
          try {
            if (valueToken.length() > 0)
              putWithValidation(nameToken, URLDecoder.decode(valueToken, "UTF-8"));
          } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e); // can't happen
          }
        }
      }
    }

    // Note to the future: when you want to implement 'req-expires' have a look at commit
    // 410a53791841
    // which had it in.
  }
 /**
  * Returns the outstanding amount of money sent back to us for all channels to this server added
  * together.
  */
 public Coin getBalanceForServer(Sha256Hash id) {
   Coin balance = Coin.ZERO;
   lock.lock();
   try {
     Set<StoredClientChannel> setChannels = mapChannels.get(id);
     for (StoredClientChannel channel : setChannels) {
       synchronized (channel) {
         if (channel.close != null) continue;
         balance = balance.add(channel.valueToMe);
       }
     }
     return balance;
   } finally {
     lock.unlock();
   }
 }
Exemplo n.º 8
0
 /** Returns a {@link Wallet.SendRequest} suitable for broadcasting to the network. */
 public Wallet.SendRequest getSendRequest() {
   Transaction tx = new Transaction(params);
   for (Protos.Output output : paymentDetails.getOutputsList())
     tx.addOutput(
         new TransactionOutput(
             params, tx, Coin.valueOf(output.getAmount()), output.getScript().toByteArray()));
   return Wallet.SendRequest.forTx(tx).fromPaymentDetails(paymentDetails);
 }
Exemplo n.º 9
0
  /**
   * Simple Bitcoin URI builder using known good fields.
   *
   * @param params The network parameters that determine which network the URI is for.
   * @param address The Bitcoin address
   * @param amount The amount
   * @param label A label
   * @param message A message
   * @return A String containing the Bitcoin URI
   */
  public static String convertToBitcoinURI(
      NetworkParameters params,
      String address,
      @Nullable Coin amount,
      @Nullable String label,
      @Nullable String message) {
    checkNotNull(params);
    checkNotNull(address);
    if (amount != null && amount.signum() < 0) {
      throw new IllegalArgumentException("Coin must be positive");
    }

    StringBuilder builder = new StringBuilder();
    String scheme = params.getUriScheme();
    builder.append(scheme).append(":").append(address);

    boolean questionMarkHasBeenOutput = false;

    if (amount != null) {
      builder.append(QUESTION_MARK_SEPARATOR).append(FIELD_AMOUNT).append("=");
      builder.append(amount.toPlainString());
      questionMarkHasBeenOutput = true;
    }

    if (label != null && !"".equals(label)) {
      if (questionMarkHasBeenOutput) {
        builder.append(AMPERSAND_SEPARATOR);
      } else {
        builder.append(QUESTION_MARK_SEPARATOR);
        questionMarkHasBeenOutput = true;
      }
      builder.append(FIELD_LABEL).append("=").append(encodeURLString(label));
    }

    if (message != null && !"".equals(message)) {
      if (questionMarkHasBeenOutput) {
        builder.append(AMPERSAND_SEPARATOR);
      } else {
        builder.append(QUESTION_MARK_SEPARATOR);
      }
      builder.append(FIELD_MESSAGE).append("=").append(encodeURLString(message));
    }

    return builder.toString();
  }
Exemplo n.º 10
0
 /** Returns the outputs of the payment request. */
 public List<PaymentProtocol.Output> getOutputs() {
   List<PaymentProtocol.Output> outputs =
       new ArrayList<PaymentProtocol.Output>(paymentDetails.getOutputsCount());
   for (Protos.Output output : paymentDetails.getOutputsList()) {
     Coin amount = output.hasAmount() ? Coin.valueOf(output.getAmount()) : null;
     outputs.add(new PaymentProtocol.Output(amount, output.getScript().toByteArray()));
   }
   return outputs;
 }
Exemplo n.º 11
0
  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");
    }
  }
 @Override
 public void deserializeWalletExtension(Wallet containingWallet, byte[] data) throws Exception {
   lock.lock();
   try {
     checkState(this.containingWallet == null || this.containingWallet == containingWallet);
     this.containingWallet = containingWallet;
     NetworkParameters params = containingWallet.getParams();
     ClientState.StoredClientPaymentChannels states =
         ClientState.StoredClientPaymentChannels.parseFrom(data);
     for (ClientState.StoredClientPaymentChannel storedState : states.getChannelsList()) {
       Transaction refundTransaction =
           params
               .getDefaultSerializer()
               .makeTransaction(storedState.getRefundTransaction().toByteArray());
       refundTransaction.getConfidence().setSource(TransactionConfidence.Source.SELF);
       ECKey myKey =
           (storedState.getMyKey().isEmpty())
               ? containingWallet.findKeyFromPubKey(storedState.getMyPublicKey().toByteArray())
               : ECKey.fromPrivate(storedState.getMyKey().toByteArray());
       StoredClientChannel channel =
           new StoredClientChannel(
               Sha256Hash.wrap(storedState.getId().toByteArray()),
               params
                   .getDefaultSerializer()
                   .makeTransaction(storedState.getContractTransaction().toByteArray()),
               refundTransaction,
               myKey,
               Coin.valueOf(storedState.getValueToMe()),
               Coin.valueOf(storedState.getRefundFees()),
               false);
       if (storedState.hasCloseTransactionHash()) {
         Sha256Hash closeTxHash =
             Sha256Hash.wrap(storedState.getCloseTransactionHash().toByteArray());
         channel.close = containingWallet.getTransaction(closeTxHash);
       }
       putChannel(channel, false);
     }
   } finally {
     lock.unlock();
   }
 }
Exemplo n.º 13
0
  @Override
  protected void run() {
    try {
      runInterceptHook();
      checkNotNull(trade.getTradeAmount(), "trade.getTradeAmount() must not be null");
      Coin sellerPayoutAmount = FeePolicy.SECURITY_DEPOSIT;
      Coin buyerPayoutAmount = sellerPayoutAmount.add(trade.getTradeAmount());

      // We use the sellers LastBlockSeenHeight, which might be different to the buyers one.
      // If lock time is 0 we set lockTimeAsBlockHeight to 0 to mark it as "not set".
      // In the tradewallet we apply the locktime only if it is set, otherwise we use the default
      // values for
      // transaction locktime and sequence number
      long lockTime = trade.getOffer().getPaymentMethod().getLockTime();
      long lockTimeAsBlockHeight = 0;
      if (lockTime > 0)
        lockTimeAsBlockHeight =
            processModel.getTradeWalletService().getLastBlockSeenHeight() + lockTime;
      trade.setLockTimeAsBlockHeight(lockTimeAsBlockHeight);

      byte[] payoutTxSignature =
          processModel
              .getTradeWalletService()
              .sellerSignsPayoutTx(
                  trade.getDepositTx(),
                  buyerPayoutAmount,
                  sellerPayoutAmount,
                  processModel.tradingPeer.getPayoutAddressString(),
                  processModel.getAddressEntry(),
                  lockTimeAsBlockHeight,
                  processModel.tradingPeer.getTradeWalletPubKey(),
                  processModel.getTradeWalletPubKey(),
                  processModel.getArbitratorPubKey(trade.getArbitratorAddress()));

      processModel.setPayoutTxSignature(payoutTxSignature);

      complete();
    } catch (Throwable t) {
      failed(t);
    }
  }
Exemplo n.º 14
0
 /**
  * Create a standard pay to address output for usage in {@link #createPaymentRequest} and {@link
  * #createPaymentMessage}.
  *
  * @param amount amount to pay, or null
  * @param address address to pay to
  * @return output
  */
 public static Protos.Output createPayToAddressOutput(@Nullable Coin amount, Address address) {
   Protos.Output.Builder output = Protos.Output.newBuilder();
   if (amount != null) {
     final NetworkParameters params = address.getParameters();
     if (params.hasMaxMoney() && amount.compareTo(params.getMaxMoney()) > 0)
       throw new IllegalArgumentException("Amount too big: " + amount);
     output.setAmount(amount.value);
   } else {
     output.setAmount(0);
   }
   output.setScript(ByteString.copyFrom(ScriptBuilder.createOutputScript(address).getProgram()));
   return output.build();
 }
Exemplo n.º 15
0
 public Coin getRecommendedFee() {
   String url = "https://bitcoinfees.21.co/api/v1/fees/recommended";
   URL obj;
   try {
     obj = new URL(url);
     JSONTokener tokener;
     HttpURLConnection con = (HttpURLConnection) obj.openConnection();
     con.setRequestMethod("GET");
     // set user header to prevent 403
     con.setRequestProperty("User-Agent", "Chrome/5.0");
     tokener = new JSONTokener(con.getInputStream());
     JSONObject root = new JSONObject(tokener);
     return Coin.valueOf(Long.valueOf(root.get("fastestFee").toString()));
   } catch (IOException e) {
     e.printStackTrace();
     throw new RuntimeException(e);
   }
 }
Exemplo n.º 16
0
 public Transaction send(String destinationAddress, long amountSatoshis)
     throws InsufficientMoneyException {
   Address addressj;
   try {
     addressj = new Address(params, destinationAddress);
   } catch (AddressFormatException e) {
     e.printStackTrace();
     throw new RuntimeException(e);
   }
   Coin amount = Coin.valueOf(amountSatoshis);
   // create a tx to destinationAddress of amount and broadcast by wallets peergroup
   Wallet.SendResult sendResult = getKit().wallet().sendCoins(kit.peerGroup(), addressj, amount);
   try {
     return sendResult.broadcastComplete.get();
   } catch (InterruptedException | ExecutionException e) {
     e.printStackTrace();
     throw new RuntimeException(e);
   }
 }
Exemplo n.º 17
0
 public String sendOffline(String destinationAddress, long amountSatoshis)
     throws InsufficientMoneyException {
   Address addressj;
   try {
     addressj = new Address(params, destinationAddress);
   } catch (AddressFormatException e) {
     e.printStackTrace();
     throw new RuntimeException(e);
   }
   Coin amount = Coin.valueOf(amountSatoshis);
   // create a SendRequest of amount to destinationAddress
   Wallet.SendRequest sendRequest = Wallet.SendRequest.to(addressj, amount);
   // set dynamic fee
   sendRequest.feePerKb = getRecommendedFee();
   // complete & sign tx
   kit.wallet().completeTx(sendRequest);
   kit.wallet().signTransaction(sendRequest);
   // return tx bytes as hex encoded String
   return Hex.encodeHexString(sendRequest.tx.bitcoinSerialize());
 }
 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;
   }
 }
  @Test
  public void testUTXOProviderWithWallet() throws Exception {
    final int UNDOABLE_BLOCKS_STORED = 10;
    store = createStore(params, UNDOABLE_BLOCKS_STORED);
    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);
    Transaction transaction = rollingBlock.getTransactions().get(0);
    TransactionOutPoint spendableOutput = new TransactionOutPoint(params, 0, transaction.getHash());
    byte[] spendableOutputScriptPubKey = transaction.getOutputs().get(0).getScriptBytes();
    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);

    // Create 1 BTC spend to a key in this wallet (to ourselves).
    Wallet wallet = new Wallet(params);
    assertEquals(
        "Available balance is incorrect",
        Coin.ZERO,
        wallet.getBalance(Wallet.BalanceType.AVAILABLE));
    assertEquals(
        "Estimated balance is incorrect",
        Coin.ZERO,
        wallet.getBalance(Wallet.BalanceType.ESTIMATED));

    wallet.setUTXOProvider(store);
    ECKey toKey = wallet.freshReceiveKey();
    Coin amount = Coin.valueOf(100000000);

    Transaction t = new Transaction(params);
    t.addOutput(new TransactionOutput(params, t, amount, toKey));
    t.addSignedInput(spendableOutput, new Script(spendableOutputScriptPubKey), outKey);
    rollingBlock.addTransaction(t);
    rollingBlock.solve();
    chain.add(rollingBlock);

    // Create another spend of 1/2 the value of BTC we have available using the wallet (store coin
    // selector).
    ECKey toKey2 = new ECKey();
    Coin amount2 = amount.divide(2);
    Address address2 = new Address(params, toKey2.getPubKeyHash());
    Wallet.SendRequest req = Wallet.SendRequest.to(address2, amount2);
    wallet.completeTx(req);
    wallet.commitTx(req.tx);
    Coin fee = req.fee;

    // There should be one pending tx (our spend).
    assertEquals(
        "Wrong number of PENDING.4", 1, wallet.getPoolSize(WalletTransaction.Pool.PENDING));
    Coin totalPendingTxAmount = Coin.ZERO;
    for (Transaction tx : wallet.getPendingTransactions()) {
      totalPendingTxAmount = totalPendingTxAmount.add(tx.getValueSentToMe(wallet));
    }

    // The availbale balance should be the 0 (as we spent the 1 BTC that's pending) and estimated
    // should be 1/2 - fee BTC
    assertEquals(
        "Available balance is incorrect",
        Coin.ZERO,
        wallet.getBalance(Wallet.BalanceType.AVAILABLE));
    assertEquals(
        "Estimated balance is incorrect",
        amount2.subtract(fee),
        wallet.getBalance(Wallet.BalanceType.ESTIMATED));
    assertEquals("Pending tx amount is incorrect", amount2.subtract(fee), totalPendingTxAmount);
    try {
      store.close();
    } catch (Exception e) {
    }
  }
  /**
   * function that updates debit/credit/balance/price in fiat and Transaction History table
   *
   * @param services
   */
  private void updateWalletsSummary(List<WalletService> services) {
    Coin totalDebit = Coin.ZERO;
    Coin totalCredit = Coin.ZERO;
    Coin totalBalance = Coin.ZERO;
    double priceInFiat = 0.00d;
    String confidence = "";
    // update debit/credit/balance and price in fiat
    List<TransactionWrapper> transactions = new ArrayList<>();
    for (WalletService service : services) {
      try {
        Wallet wallet = service.getWallet();
        totalBalance = totalBalance.add(wallet.getBalance());
        for (Transaction trx : wallet.getTransactionsByTime()) {
          if (trx.getConfidence().equals(TransactionConfidence.ConfidenceType.DEAD)) continue;
          Coin amount = trx.getValue(wallet);
          if (amount.isPositive()) {
            totalCredit = totalCredit.add(amount);
          } else {
            totalDebit = totalDebit.add(amount);
          }
          transactions.add(new TransactionWrapper(trx, wallet, amount));
        }
      } catch (Exception e) {
        logger.error("Unable to update wallet details");
      }
    }
    pnlDashboardStats.setTotalBalance(MonetaryFormat.BTC.noCode().format(totalBalance).toString());
    // pnlDashboardStats.setTotalDebit(MonetaryFormat.BTC.noCode().format(totalDebit).toString());
    // pnlDashboardStats.setTotalCredit(MonetaryFormat.BTC.noCode().format(totalCredit).toString());
    priceInFiat = Double.valueOf(MonetaryFormat.BTC.noCode().format(totalBalance).toString());
    priceInFiat *= BitcoinCurrencyRateApi.get().getCurrentRateValue();
    pnlDashboardStats.setPriceInFiat(
        String.format("%.2f", priceInFiat), "", ConfigManager.config().getSelectedCurrency());
    pnlDashboardStats.setExchangeRate(
        ConfigManager.config().getSelectedCurrency(),
        String.format("%.2f", BitcoinCurrencyRateApi.get().getCurrentRateValue()));
    Collections.sort(
        transactions,
        new Comparator<TransactionWrapper>() {
          @Override
          public int compare(TransactionWrapper o1, TransactionWrapper o2) {
            return o2.getTransaction()
                .getUpdateTime()
                .compareTo(o1.getTransaction().getUpdateTime());
          }
        });
    // update Transaction History table
    DefaultTableModel model = (DefaultTableModel) tblTransactions.getModel();
    model.setRowCount(0);
    for (TransactionWrapper wrapper : transactions) {
      Transaction transaction = wrapper.getTransaction();
      if (transaction.getConfidence().getDepthInBlocks() > 6)
        confidence = "<html>6<sup>+</sup></html>";
      else confidence = transaction.getConfidence().getDepthInBlocks() + "";
      Coin amount = wrapper.getAmount();
      Coin fee = transaction.getFee();
      String amountString = MonetaryFormat.BTC.noCode().format(amount).toString();
      String feeString = fee != null ? MonetaryFormat.BTC.noCode().format(fee).toString() : "0.00";
      Address from = transaction.getInput(0).getFromAddress();
      Address to =
          transaction
              .getOutput(0)
              .getAddressFromP2PKHScript(wrapper.getWallet().getNetworkParameters());
      boolean credit = amount.isPositive();

      model.addRow(
          new Object[] {
            Utils.formatTransactionDate(transaction.getUpdateTime()),
            from,
            to,
            credit ? "Credit" : "Debit",
            amountString,
            feeString,
            "",
            confidence
          });
    }
    Coin balanceAfter = Coin.ZERO;
    for (int index = transactions.size() - 1; index >= 0; index--) {
      balanceAfter = balanceAfter.add(Coin.parseCoin((String) model.getValueAt(index, 4)));
      model.setValueAt(MonetaryFormat.BTC.noCode().format(balanceAfter).toString(), index, 6);
    }
  }
  /**
   * 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;
  }
  /**
   * 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;
  }
  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);
  }