/**
   * Calculate the balance for a coinbase, to-address, or p2sh address.
   *
   * @param address The address to calculate the balance of
   * @return The balance of the address supplied. If the address has not been seen, or there are no
   *     outputs open for this address, the return value is 0
   * @throws BlockStoreException
   */
  public BigInteger calculateBalanceForAddress(Address address) throws BlockStoreException {
    maybeConnect();
    PreparedStatement s = null;

    try {
      s =
          conn.get()
              .prepareStatement(
                  "select sum(('x'||lpad(substr(value::text, 3, 50),16,'0'))::bit(64)::bigint) "
                      + "from openoutputs where toaddress = ?");
      s.setString(1, address.toString());
      ResultSet rs = s.executeQuery();
      if (rs.next()) {
        return BigInteger.valueOf(rs.getLong(1));
      } else {
        throw new BlockStoreException("Failed to execute balance lookup");
      }

    } catch (SQLException ex) {
      throw new BlockStoreException(ex);
    } finally {
      if (s != null)
        try {
          s.close();
        } catch (SQLException e) {
          throw new BlockStoreException("Could not close statement");
        }
    }
  }
  public void addUnspentTransactionOutput(StoredTransactionOutput out) throws BlockStoreException {
    maybeConnect();
    PreparedStatement s = null;

    // Calculate the toAddress (if any)
    String dbAddress = "";
    int type = 0;
    Script outputScript = null;
    try {
      outputScript = new Script(out.getScriptBytes());
    } catch (ScriptException e) {
      // Unparseable, but this isn't an error - it's an output not containing an address
      log.info("Could not parse script for output: " + out.getHash().toString());
    }
    if (outputScript != null
        && (outputScript.isSentToAddress()
            || outputScript.isSentToRawPubKey()
            || outputScript.isPayToScriptHash())) {
      if (outputScript.isSentToAddress()) {
        Address targetAddr = new Address(params, outputScript.getPubKeyHash());
        dbAddress = targetAddr.toString();
        type = 1;
      } else if (outputScript.isSentToRawPubKey()) {
        /*
         *   Note we use the deprecated getFromAddress here.  Coinbase outputs seem to have the target address
         *   in the pubkey of the script - perhaps we can rename this function?
         */

        dbAddress = outputScript.getFromAddress(params).toString();
        type = 2;
      } else if (outputScript.isPayToScriptHash()) {
        dbAddress = Address.fromP2SHHash(params, outputScript.getPubKeyHash()).toString();
        type = 3;
      }
    }

    try {
      s =
          conn.get()
              .prepareStatement(
                  "INSERT INTO openOutputs (hash, index, height, value, scriptBytes, toAddress, addressTargetable) "
                      + "VALUES (?, ?, ?, ?, ?, ?, ?)");
      s.setBytes(1, out.getHash().getBytes());
      // index is actually an unsigned int
      s.setInt(2, (int) out.getIndex());
      s.setInt(3, out.getHeight());
      s.setBytes(4, out.getValue().toByteArray());
      s.setBytes(5, out.getScriptBytes());
      s.setString(6, dbAddress);
      s.setInt(7, type);
      s.executeUpdate();
      s.close();
    } catch (SQLException e) {
      if (!(e.getSQLState().equals(POSTGRES_DUPLICATE_KEY_ERROR_CODE)))
        throw new BlockStoreException(e);
    } finally {
      if (s != null)
        try {
          s.close();
        } catch (SQLException e) {
          throw new BlockStoreException(e);
        }
    }
  }