Beispiel #1
0
  /**
   * Broadcast outgoing transactions.
   *
   * <p>This method should only be called from the wallet manager
   *
   * @return false if synchronization failed due to failed blockchain connection
   */
  public synchronized boolean broadcastOutgoingTransactions() {
    checkNotArchived();
    List<Sha256Hash> broadcastedIds = new LinkedList<Sha256Hash>();
    Map<Sha256Hash, byte[]> transactions = _backing.getOutgoingTransactions();

    for (byte[] rawTransaction : transactions.values()) {
      TransactionEx tex = TransactionEx.fromUnconfirmedTransaction(rawTransaction);

      BroadcastResult result = broadcastTransaction(TransactionEx.toTransaction(tex));
      if (result == BroadcastResult.SUCCESS) {
        broadcastedIds.add(tex.txid);
        _backing.removeOutgoingTransaction(tex.txid);
      } else {
        if (result == BroadcastResult.REJECTED) {
          // invalid tx
          _backing.deleteTransaction(tex.txid);
          _backing.removeOutgoingTransaction(tex.txid);
        } else {
          // No connection --> retry next sync
        }
      }
    }
    if (!broadcastedIds.isEmpty()) {
      onTransactionsBroadcasted(broadcastedIds);
    }
    return true;
  }
Beispiel #2
0
 @Override
 public synchronized boolean deleteTransaction(Sha256Hash transactionId) {
   TransactionEx tex = _backing.getTransaction(transactionId);
   if (tex == null) return false;
   Transaction tx = TransactionEx.toTransaction(tex);
   _backing.beginTransaction();
   try {
     // See if any of the outputs are stored locally and remove them
     for (int i = 0; i < tx.outputs.length; i++) {
       TransactionOutput output = tx.outputs[i];
       OutPoint outPoint = new OutPoint(tx.getHash(), i);
       TransactionOutputEx utxo = _backing.getUnspentOutput(outPoint);
       if (utxo != null) {
         _backing.deleteUnspentOutput(outPoint);
       }
     }
     // remove it from the backing
     _backing.deleteTransaction(transactionId);
     _backing.setTransactionSuccessful();
   } finally {
     _backing.endTransaction();
   }
   updateLocalBalance(); // will still need a new sync besides re-calculating
   return true;
 }
Beispiel #3
0
  protected boolean monitorYoungTransactions() {
    Collection<TransactionEx> list = _backing.getYoungTransactions(5, getBlockChainHeight());
    if (list.isEmpty()) {
      return true;
    }
    List<Sha256Hash> txids = new ArrayList<Sha256Hash>(list.size());
    for (TransactionEx tex : list) {
      txids.add(tex.txid);
    }
    CheckTransactionsResponse result;
    try {
      result = _wapi.checkTransactions(new CheckTransactionsRequest(txids)).getResult();
    } catch (WapiException e) {
      postEvent(Event.SERVER_CONNECTION_ERROR);
      _logger.logError("Server connection failed with error code: " + e.errorCode, e);
      // We failed to check transactions
      return false;
    }
    for (TransactionStatus t : result.transactions) {
      if (!t.found) {
        // We have a transaction locally that does not exist in the
        // blockchain. Must be a residue due to double-spend or malleability

        _backing.deleteTransaction(t.txid);
        continue;
      }
      TransactionEx tex = _backing.getTransaction(t.txid);
      Preconditions.checkNotNull(tex);
      if (tex.height != t.height || tex.time != t.time) {
        // The transaction got a new height or timestamp. There could be
        // several reasons for that. It got a new timestamp from the server,
        // it confirmed, or might also be a reorg.
        TransactionEx newTex = new TransactionEx(tex.txid, t.height, t.time, tex.binary);
        System.out.println("Replacing:\n" + tex.toString() + "\nWith:\n" + newTex.toString());
        postEvent(Event.TRANSACTION_HISTORY_CHANGED);
        _backing.deleteTransaction(tex.txid);
        _backing.putTransaction(newTex);
      }
    }
    return true;
  }
Beispiel #4
0
  @Override
  public synchronized boolean cancelQueuedTransaction(Sha256Hash transaction) {
    Map<Sha256Hash, byte[]> outgoingTransactions = _backing.getOutgoingTransactions();

    if (!outgoingTransactions.containsKey(transaction)) {
      return false;
    }

    Transaction tx;
    try {
      tx = Transaction.fromBytes(outgoingTransactions.get(transaction));
    } catch (TransactionParsingException e) {
      return false;
    }

    _backing.beginTransaction();
    try {

      // See if any of the outputs are stored locally and remove them
      for (int i = 0; i < tx.outputs.length; i++) {
        TransactionOutput output = tx.outputs[i];
        OutPoint outPoint = new OutPoint(tx.getHash(), i);
        TransactionOutputEx utxo = _backing.getUnspentOutput(outPoint);
        if (utxo != null) {
          _backing.deleteUnspentOutput(outPoint);
        }
      }

      // Remove a queued transaction from our outgoing buffer
      _backing.removeOutgoingTransaction(transaction);

      // remove it from the backing
      _backing.deleteTransaction(transaction);
      _backing.setTransactionSuccessful();
    } finally {
      _backing.endTransaction();
    }

    // calc the new balance to remove the outgoing amount
    // the total balance will still be wrong, as we already deleted some UTXOs to build the queued
    // transaction
    // these will get restored after the next sync
    updateLocalBalance();

    // markTransactionAsSpent(transaction);
    return true;
  }