@Override
  public TransactionDetails getTransactionDetails(Sha256Hash txid) {
    // 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.

    TransactionEx tex = _backing.getTransaction(txid);
    Transaction tx = TransactionEx.toTransaction(tex);
    if (tx == null) {
      throw new RuntimeException();
    }

    List<TransactionDetails.Item> inputs = new ArrayList<TransactionDetails.Item>(tx.inputs.length);
    if (tx.isCoinbase()) {
      // We have a coinbase transaction. Create one input with the sum of the outputs as its value,
      // and make the address the null address
      long value = 0;
      for (TransactionOutput out : tx.outputs) {
        value += out.value;
      }
      inputs.add(new TransactionDetails.Item(Address.getNullAddress(_network), value, true));
    } else {
      // Populate the inputs
      for (TransactionInput input : tx.inputs) {
        Sha256Hash parentHash = input.outPoint.hash;
        // Get the parent transaction
        TransactionOutputEx parentOutput = _backing.getParentTransactionOutput(input.outPoint);
        if (parentOutput == null) {
          // We never heard about the parent, skip
          continue;
        }
        // Determine the parent address
        Address parentAddress;
        ScriptOutput parentScript = ScriptOutput.fromScriptBytes(parentOutput.script);
        if (parentScript == null) {
          // Null address means we couldn't figure out the address, strange script
          parentAddress = Address.getNullAddress(_network);
        } else {
          parentAddress = parentScript.getAddress(_network);
        }
        inputs.add(new TransactionDetails.Item(parentAddress, parentOutput.value, false));
      }
    }
    // Populate the outputs
    TransactionDetails.Item[] outputs = new TransactionDetails.Item[tx.outputs.length];
    for (int i = 0; i < tx.outputs.length; i++) {
      Address address = tx.outputs[i].script.getAddress(_network);
      outputs[i] = new TransactionDetails.Item(address, tx.outputs[i].value, false);
    }

    return new TransactionDetails(
        txid, tex.height, tex.time, inputs.toArray(new TransactionDetails.Item[] {}), outputs);
  }
  protected TransactionSummary transform(
      Transaction tx, int time, int height, int blockChainHeight) {
    long value = 0;
    Address destAddress = null;
    for (TransactionOutput output : tx.outputs) {
      if (isMine(output.script)) {
        value += output.value;
      } else {
        destAddress = output.script.getAddress(_network);
      }
    }

    if (tx.isCoinbase()) {
      // For coinbase transactions there is nothing to subtract
    } else {
      for (TransactionInput input : tx.inputs) {
        // find parent output
        TransactionOutputEx funding = _backing.getParentTransactionOutput(input.outPoint);
        if (funding == null) {
          _logger.logError("Unable to find parent output for: " + input.outPoint);
          continue;
        }
        if (isMine(funding)) {
          value -= funding.value;
        }
      }
    }

    int confirmations;
    if (height == -1) {
      confirmations = 0;
    } else {
      confirmations = Math.max(0, blockChainHeight - height + 1);
    }

    // only track a destinationAddress if it is an outgoing transaction (i.e. send money to someone)
    // to prevent the user that he tries to return money to an address he got bitcoin from.
    if (value >= 0) {
      destAddress = null;
    }

    boolean isQueuedOutgoing = _backing.isOutgoingTransaction(tx.getHash());

    return new TransactionSummary(
        tx.getHash(),
        value,
        time,
        height,
        confirmations,
        isQueuedOutgoing,
        com.google.common.base.Optional.fromNullable(destAddress));
  }