예제 #1
1
  protected Collection<TransactionOutputEx> getSpendableOutputs() {
    Collection<TransactionOutputEx> list = _backing.getAllUnspentOutputs();

    // Prune confirmed outputs for coinbase outputs that are not old enough
    // for spending. Also prune unconfirmed receiving coins except for change
    int blockChainHeight = getBlockChainHeight();
    Iterator<TransactionOutputEx> it = list.iterator();
    while (it.hasNext()) {
      TransactionOutputEx output = it.next();
      if (output.isCoinBase) {
        int confirmations = blockChainHeight - output.height;
        if (confirmations < COINBASE_MIN_CONFIRMATIONS) {
          it.remove();
          continue;
        }
      }
      // Unless we allow zero confirmation spending we prune all unconfirmed outputs sent from
      // foreign addresses
      if (!_allowZeroConfSpending) {
        if (output.height == -1 && !isFromMe(output.outPoint.hash)) {
          // Prune receiving coins that is not change sent to ourselves
          it.remove();
        }
      }
    }
    return list;
  }
예제 #2
0
  @Override
  public List<TransactionOutputSummary> getUnspentTransactionOutputSummary() {
    // Note that this method is not synchronized, and we might fetch the transaction history while
    // synchronizing
    // accounts. That should be ok as we write to the DB in a sane order.

    // Get all unspent outputs for this account
    Collection<TransactionOutputEx> outputs = _backing.getAllUnspentOutputs();

    // Transform it to a list of summaries
    List<TransactionOutputSummary> list = new ArrayList<TransactionOutputSummary>();
    int blockChainHeight = getBlockChainHeight();
    for (TransactionOutputEx output : outputs) {

      ScriptOutput script = ScriptOutput.fromScriptBytes(output.script);
      Address address;
      if (script == null) {
        address = Address.getNullAddress(_network);
        // This never happens as we have parsed this script before
      } else {
        address = script.getAddress(_network);
      }
      int confirmations;
      if (output.height == -1) {
        confirmations = 0;
      } else {
        confirmations = Math.max(0, blockChainHeight - output.height + 1);
      }

      TransactionOutputSummary summary =
          new TransactionOutputSummary(
              output.outPoint, output.value, output.height, confirmations, address);
      list.add(summary);
    }
    // Sort & return
    Collections.sort(list);
    return list;
  }
예제 #3
0
  protected Balance calculateLocalBalance() {

    Collection<TransactionOutputEx> unspentOutputs =
        new HashSet<TransactionOutputEx>(_backing.getAllUnspentOutputs());
    long confirmed = 0;
    long pendingChange = 0;
    long pendingSending = 0;
    long pendingReceiving = 0;

    //
    // Determine the value we are receiving and create a set of outpoints for fast lookup
    //
    Set<OutPoint> unspentOutPoints = new HashSet<OutPoint>();
    for (TransactionOutputEx output : unspentOutputs) {
      if (output.height == -1) {
        if (isFromMe(output.outPoint.hash)) {
          pendingChange += output.value;
        } else {
          pendingReceiving += output.value;
        }
      } else {
        confirmed += output.value;
      }
      unspentOutPoints.add(output.outPoint);
    }

    //
    // Determine the value we are sending
    //

    // Get the current set of unconfirmed transactions
    List<Transaction> unconfirmed = new ArrayList<Transaction>();
    for (TransactionEx tex : _backing.getUnconfirmedTransactions()) {
      try {
        Transaction t = Transaction.fromByteReader(new ByteReader(tex.binary));
        unconfirmed.add(t);
      } catch (TransactionParsingException e) {
        // never happens, we have parsed it before
      }
    }

    for (Transaction t : unconfirmed) {
      // For each input figure out if WE are sending it by fetching the
      // parent transaction and looking at the address
      boolean weSend = false;
      for (TransactionInput input : t.inputs) {
        // Find the parent transaction
        if (input.outPoint.hash.equals(Sha256Hash.ZERO_HASH)) {
          continue;
        }
        TransactionOutputEx parent = _backing.getParentTransactionOutput(input.outPoint);
        if (parent == null) {
          _logger.logError("Unable to find parent transaction output: " + input.outPoint);
          continue;
        }
        TransactionOutput parentOutput = transform(parent);
        Address fundingAddress = parentOutput.script.getAddress(_network);
        if (isMine(fundingAddress)) {
          // One of our addresses are sending coins
          pendingSending += parentOutput.value;
          weSend = true;
        }
      }

      // Now look at the outputs and if it contains change for us, then subtract that from the
      // sending amount
      // if it is already spent in another transaction
      for (int i = 0; i < t.outputs.length; i++) {
        TransactionOutput output = t.outputs[i];
        Address destination = output.script.getAddress(_network);
        if (weSend && isMine(destination)) {
          // The funds are sent from us to us
          OutPoint outPoint = new OutPoint(t.getHash(), i);
          if (!unspentOutPoints.contains(outPoint)) {
            // This output has been spent, subtract it from the amount sent
            pendingSending -= output.value;
          }
        }
      }
    }

    int blockHeight = getBlockChainHeight();
    return new Balance(
        confirmed,
        pendingReceiving,
        pendingSending,
        pendingChange,
        System.currentTimeMillis(),
        blockHeight,
        true,
        _allowZeroConfSpending);
  }
예제 #4
0
  protected boolean synchronizeUnspentOutputs(Collection<Address> addresses) {
    // Get the current unspent outputs as dictated by the block chain
    QueryUnspentOutputsResponse UnspentOutputResponse;
    try {
      UnspentOutputResponse =
          _wapi
              .queryUnspentOutputs(new QueryUnspentOutputsRequest(Wapi.VERSION, addresses))
              .getResult();
    } catch (WapiException e) {
      _logger.logError("Server connection failed with error code: " + e.errorCode, e);
      postEvent(Event.SERVER_CONNECTION_ERROR);
      return false;
    }
    Collection<TransactionOutputEx> remoteUnspent = UnspentOutputResponse.unspent;
    // Store the current block height
    setBlockChainHeight(UnspentOutputResponse.height);
    // Make a map for fast lookup
    Map<OutPoint, TransactionOutputEx> remoteMap = toMap(remoteUnspent);

    // Get the current unspent outputs as it is believed to be locally
    Collection<TransactionOutputEx> localUnspent = _backing.getAllUnspentOutputs();
    // Make a map for fast lookup
    Map<OutPoint, TransactionOutputEx> localMap = toMap(localUnspent);

    // Find remotely removed unspent outputs
    for (TransactionOutputEx l : localUnspent) {
      TransactionOutputEx r = remoteMap.get(l.outPoint);
      if (r == null) {
        // An output has gone. Maybe it was spent in another wallet, or
        // never confirmed due to missing fees, double spend, or mutated.
        // Either way, we delete it locally
        _backing.deleteUnspentOutput(l.outPoint);
      }
    }

    // Find remotely added unspent outputs
    Set<Sha256Hash> transactionsToAddOrUpdate = new HashSet<Sha256Hash>();
    List<TransactionOutputEx> unspentOutputsToAddOrUpdate = new LinkedList<TransactionOutputEx>();
    for (TransactionOutputEx r : remoteUnspent) {
      TransactionOutputEx l = localMap.get(r.outPoint);
      if (l == null || l.height != r.height) {
        // New remote output or new height (Maybe it confirmed or we
        // might even have had a reorg). Either way we just update it
        unspentOutputsToAddOrUpdate.add(r);
        transactionsToAddOrUpdate.add(r.outPoint.hash);
        // Note: We are not adding the unspent output to the DB just yet. We
        // first want to verify the full set of funding transactions of the
        // transaction that this unspent output belongs to
      }
    }

    // Fetch updated or added transactions
    if (transactionsToAddOrUpdate.size() > 0) {
      GetTransactionsResponse response;
      try {
        response =
            _wapi
                .getTransactions(
                    new GetTransactionsRequest(Wapi.VERSION, transactionsToAddOrUpdate))
                .getResult();
      } catch (WapiException e) {
        _logger.logError("Server connection failed with error code: " + e.errorCode, e);
        postEvent(Event.SERVER_CONNECTION_ERROR);
        return false;
      }
      try {
        handleNewExternalTransactions(response.transactions);
      } catch (WapiException e) {
        _logger.logError("Server connection failed with error code: " + e.errorCode, e);
        postEvent(Event.SERVER_CONNECTION_ERROR);
        return false;
      }
      // Finally update out list of unspent outputs with added or updated
      // outputs
      for (TransactionOutputEx output : unspentOutputsToAddOrUpdate) {
        _backing.putUnspentOutput(output);
      }
    }

    return true;
  }