@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);
  }
 @Override
 public BitcoinSigner findSignerByPublicKey(PublicKey publicKey) {
   Address address = publicKey.toAddress(_network);
   InMemoryPrivateKey privateKey;
   try {
     privateKey = getPrivateKeyForAddress(address, _cipher);
   } catch (InvalidKeyCipher e) {
     throw new RuntimeException(
         "Unable to decrypt private key for address " + address.toString());
   }
   if (privateKey != null) {
     return privateKey;
   }
   throw new RuntimeException("Unable to find private key for address " + address.toString());
 }
 @Override
 public PublicKey findPublicKeyByAddress(Address address) {
   PublicKey publicKey = getPublicKeyForAddress(address);
   if (publicKey != null) {
     return publicKey;
   }
   throw new RuntimeException("Unable to find public key for address " + address.toString());
 }
  @Override
  public synchronized long calculateMaxSpendableAmount(long minerFeeToUse) {
    checkNotArchived();
    Collection<UnspentTransactionOutput> spendableOutputs = transform(getSpendableOutputs());
    long satoshis = 0;
    for (UnspentTransactionOutput output : spendableOutputs) {
      satoshis += output.value;
    }
    // Iteratively figure out whether we can send everything by subtracting
    // the miner fee for every iteration
    while (true) {
      satoshis -= minerFeeToUse;
      if (satoshis <= 0) {
        return 0;
      }

      // Create transaction builder
      StandardTransactionBuilder stb = new StandardTransactionBuilder(_network);

      // Try and add the output
      try {
        // Note, null address used here, we just use it for measuring the
        // transaction size
        stb.addOutput(Address.getNullAddress(_network), satoshis);
      } catch (OutputTooSmallException e1) {
        // The amount we try to send is lower than what the network allows
        return 0;
      }

      // Try to create an unsigned transaction
      try {
        stb.createUnsignedTransaction(
            spendableOutputs, getChangeAddress(), new PublicKeyRing(), _network, minerFeeToUse);
        // We have enough to pay the fees, return the amount as the maximum
        return satoshis;
      } catch (InsufficientFundsException e) {
        // We cannot send this amount, try again with a little higher fee
        continue;
      }
    }
  }
  @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;
  }
 protected static UUID addressToUUID(Address address) {
   return new UUID(
       BitUtils.uint64ToLong(address.getAllAddressBytes(), 1),
       BitUtils.uint64ToLong(address.getAllAddressBytes(), 9));
 }