Example #1
0
  private void markTransactionAsSpent(Transaction transaction) {
    _backing.beginTransaction();
    try {
      // Remove inputs from unspent, marking them as spent
      for (TransactionInput input : transaction.inputs) {
        TransactionOutputEx parentOutput = _backing.getUnspentOutput(input.outPoint);
        if (parentOutput != null) {
          _backing.deleteUnspentOutput(input.outPoint);
          _backing.putParentTransactionOutput(parentOutput);
        }
      }

      // See if any of the outputs are for ourselves and store them as
      // unspent
      for (int i = 0; i < transaction.outputs.length; i++) {
        TransactionOutput output = transaction.outputs[i];
        if (isMine(output.script)) {
          _backing.putUnspentOutput(
              new TransactionOutputEx(
                  new OutPoint(transaction.getHash(), i),
                  -1,
                  output.value,
                  output.script.getScriptBytes(),
                  false));
        }
      }

      // Store transaction locally, so we have it in our history and don't
      // need to fetch it in a minute
      _backing.putTransaction(TransactionEx.fromUnconfirmedTransaction(transaction));
      _backing.setTransactionSuccessful();
    } finally {
      _backing.endTransaction();
    }

    // Tell account that we have a new transaction
    onNewTransaction(TransactionEx.fromUnconfirmedTransaction(transaction), transaction);

    // Calculate local balance cache. It has changed because we have done
    // some spending
    updateLocalBalance();
    persistContextIfNecessary();
  }
Example #2
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;
  }