BEGIN;
SELECT _v.register_patch('auditor-0002', NULL, NULL);
SET search_path TO auditor;
DO $$ BEGIN
    CREATE TYPE taler_amount
      AS
      (val INT8
      ,frac INT4
      );
    COMMENT ON TYPE taler_amount
      IS 'Stores an amount, fraction is in units of 1/100000000 of the base value';
EXCEPTION
    WHEN duplicate_object THEN null;
END $$;
CREATE TABLE auditor_amount_arithmetic_inconsistency
(
    row_id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    problem_row_id BIGINT NOT NULL,
    operation TEXT NOT NULL,
    exchange_amount taler_amount NOT NULL,
    auditor_amount taler_amount NOT NULL,
    profitable BOOLEAN NOT NULL,
    suppressed BOOLEAN NOT NULL DEFAULT FALSE
);
COMMENT ON TABLE auditor_amount_arithmetic_inconsistency
    IS 'Report a (serious) inconsistency in the exchange''s database with respect to calculations involving amounts';
CREATE TABLE IF NOT EXISTS auditor_bad_sig_losses
(
    row_id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    problem_row_id BIGINT NOT NULL,
    operation TEXT NOT NULL,
    loss taler_amount NOT NULL,
    operation_specific_pub BYTEA NOT NULL CHECK (LENGTH(operation_specific_pub)=32),
    suppressed BOOLEAN NOT NULL DEFAULT FALSE
);
COMMENT ON TABLE auditor_bad_sig_losses
    IS 'Report a (serious) inconsistency with losses due to bad signatures';
CREATE TABLE IF NOT EXISTS auditor_balances
(
   row_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
   balance_key TEXT PRIMARY KEY NOT NULL,
   balance_value taler_amount
);
COMMENT
ON TABLE auditor_balances
  IS 'table storing various global balances of the auditor';
COMMENT
ON COLUMN auditor_balances.balance_key
 IS 'unique name for the balance value';
COMMENT
ON COLUMN auditor_balances.balance_value
 IS 'balance amount';
CREATE TABLE IF NOT EXISTS auditor_closure_lags
(
    row_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE PRIMARY KEY,
    problem_row_id BIGINT NOT NULL,
    amount taler_amount NOT NULL,
    deadline BIGINT NOT NULL,
    wtid BYTEA NOT NULL CHECK (LENGTH(wtid)=32),
    account TEXT NOT NULL,
    suppressed BOOLEAN NOT NULL DEFAULT FALSE
);
COMMENT ON TABLE auditor_closure_lags
    IS 'Report closure lags.';
CREATE TABLE IF NOT EXISTS auditor_coin_inconsistency
(
    row_id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    operation TEXT NOT NULL,
    exchange_amount taler_amount NOT NULL,
    auditor_amount taler_amount NOT NULL,
    coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32),
    profitable BOOLEAN NOT NULL,
    suppressed BOOLEAN NOT NULL DEFAULT FALSE
);
COMMENT ON TABLE auditor_coin_inconsistency
    IS 'Report a (serious) inconsistency in the exchange''s database with respect to calculations involving amounts';
CREATE TABLE IF NOT EXISTS auditor_denomination_key_validity_withdraw_inconsistency
(
    row_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE PRIMARY KEY,
    problem_row_id BIGINT NOT NULL,
    execution_date BIGINT NOT NULL,
    reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_pub)=32),
    denompub_h BYTEA NOT NULL CHECK (LENGTH(denompub_h)=64),
    suppressed BOOLEAN NOT NULL DEFAULT FALSE
);
COMMENT ON TABLE auditor_denomination_key_validity_withdraw_inconsistency
    IS 'Report a (serious) denomination key validity withdraw inconsistency in the exchange''s database';
CREATE TABLE auditor_denomination_pending
  (row_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
  ,denom_pub_hash BYTEA PRIMARY KEY CHECK (LENGTH(denom_pub_hash)=64)
  ,denom_balance taler_amount NOT NULL
  ,denom_loss taler_amount NOT NULL
  ,num_issued BIGINT NOT NULL
  ,denom_risk taler_amount NOT NULL
  ,recoup_loss taler_amount NOT NULL
);
COMMENT ON TABLE auditor_denomination_pending
  IS 'outstanding denomination coins that the exchange is aware of and what the respective balances are (outstanding as well as issued overall which implies the maximum value at risk).';
COMMENT ON COLUMN auditor_denomination_pending.num_issued
  IS 'counts the number of coins issued (withdraw, refresh) of this denomination';
COMMENT ON COLUMN auditor_denomination_pending.denom_risk
  IS 'amount that could theoretically be lost in the future due to recoup operations';
COMMENT ON COLUMN auditor_denomination_pending.denom_loss
  IS 'amount that was lost due to failures by the exchange';
COMMENT ON COLUMN auditor_denomination_pending.recoup_loss
  IS 'amount actually lost due to recoup operations after a revocation';
CREATE TABLE IF NOT EXISTS auditor_denominations_without_sigs
(
    row_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE,
    denompub_h BYTEA PRIMARY KEY,
    value taler_amount NOT NULL,
    start_time BIGINT NOT NULL,
    end_time BIGINT,
    suppressed BOOLEAN NOT NULL DEFAULT FALSE
);
COMMENT ON TABLE auditor_denominations_without_sigs
    IS 'Report encountered denomination that auditor is not auditing.';
CREATE TABLE auditor_deposit_confirmations
  (row_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
  ,h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)
  ,h_policy BYTEA NOT NULL CHECK (LENGTH(h_policy)=64)
  ,h_wire BYTEA NOT NULL CHECK (LENGTH(h_wire)=64)
  ,exchange_timestamp BIGINT NOT NULL
  ,refund_deadline BIGINT NOT NULL
  ,wire_deadline BIGINT NOT NULL
  ,total_without_fee taler_amount NOT NULL
  ,coin_pubs BYTEA[] NOT NULL CHECK (CARDINALITY(coin_pubs)>0)
  ,coin_sigs BYTEA[] NOT NULL CHECK (CARDINALITY(coin_sigs)=CARDINALITY(coin_pubs))
  ,merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)
  ,exchange_sig BYTEA NOT NULL CHECK (LENGTH(exchange_sig)=64)
  ,exchange_pub BYTEA NOT NULL CHECK (LENGTH(exchange_pub)=32)
  ,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)
  ,suppressed BOOLEAN NOT NULL DEFAULT FALSE
  ,ancient BOOLEAN NOT NULL DEFAULT FALSE
  ,PRIMARY KEY (h_contract_terms,h_wire,merchant_pub,exchange_sig,exchange_pub,master_sig)
  );
COMMENT ON TABLE auditor_deposit_confirmations
  IS 'deposit confirmation sent to us by merchants; we must check that the exchange reported these properly.';
CREATE INDEX IF NOT EXISTS auditor_deposit_confirmations_not_ancient
    ON auditor_deposit_confirmations
    (exchange_timestamp ASC)
    WHERE NOT ancient;
CREATE OR REPLACE FUNCTION auditor_new_transactions_trigger()
    RETURNS trigger
    LANGUAGE plpgsql
AS $$
BEGIN
    NOTIFY XX81AFHF88YGN6ESNH39KR5VQE9MHD7GSSNMTCXB82SZ6T99ARHE0;
    RETURN NEW;
END $$;
COMMENT ON FUNCTION auditor_new_transactions_trigger()
    IS 'Call auditor_call_db_notify on new entry';
CREATE TRIGGER auditor_notify_helper_deposits
    AFTER INSERT
    ON auditor.auditor_deposit_confirmations
EXECUTE PROCEDURE auditor_new_transactions_trigger();
CREATE TABLE IF NOT EXISTS auditor_emergency
(
    row_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE PRIMARY KEY,
    denompub_h BYTEA CHECK (LENGTH(denompub_h)=64),
    denom_risk taler_amount NOT NULL,
    denom_loss taler_amount NOT NULL,
    deposit_start BIGINT NOT NULL,
    deposit_end BIGINT NOT NULL,
    value taler_amount NOT NULL,
    suppressed BOOLEAN NOT NULL DEFAULT FALSE
);
COMMENT ON TABLE auditor_emergency
    IS 'Report an emergency denomination.';
CREATE TABLE IF NOT EXISTS auditor_emergency_by_count
(
    row_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE PRIMARY KEY,
    denompub_h BYTEA NOT NULL CHECK (LENGTH(denompub_h)=64),
    num_issued BIGINT NOT NULL,
    num_known BIGINT NOT NULL,
    risk taler_amount NOT NULL,
    start BIGINT NOT NULL,
    deposit_end BIGINT NOT NULL,
    value taler_amount NOT NULL,
    suppressed BOOLEAN NOT NULL DEFAULT FALSE
);
COMMENT ON TABLE auditor_emergency_by_count
    IS 'Report an emergency denomination.';
CREATE TABLE auditor_exchange_signkeys
  (row_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
  ,exchange_pub BYTEA PRIMARY KEY CHECK (LENGTH(exchange_pub)=32)
  ,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)
  ,ep_valid_from BIGINT NOT NULL
  ,ep_expire_sign BIGINT NOT NULL
  ,ep_expire_legal BIGINT NOT NULL
  );
COMMENT ON TABLE auditor_exchange_signkeys
  IS 'list of the online signing keys of exchanges we are auditing';
COMMENT ON COLUMN auditor_exchange_signkeys.exchange_pub
  IS 'Public online signing key of the exchange.';
COMMENT ON COLUMN auditor_exchange_signkeys.master_sig
  IS 'Signature affirming the validity of the signing key of purpose TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY.';
COMMENT ON COLUMN auditor_exchange_signkeys.ep_valid_from
  IS 'Time when this online signing key will first be used to sign messages.';
COMMENT ON COLUMN auditor_exchange_signkeys.ep_expire_sign
  IS 'Time when this online signing key will no longer be used to sign.';
COMMENT ON COLUMN auditor_exchange_signkeys.ep_expire_legal
  IS 'Time when this online signing key legally expires.';
CREATE TABLE IF NOT EXISTS auditor_fee_time_inconsistency
(
    row_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE PRIMARY KEY,
    problem_row_id BIGINT NOT NULL,
    fee_type TEXT NOT NULL,
    fee_time BIGINT NOT NULL,
    diagnostic TEXT NOT NULL,
    suppressed BOOLEAN NOT NULL DEFAULT FALSE
);
COMMENT ON TABLE auditor_fee_time_inconsistency
    IS 'Report a (serious) fee time inconsistency in the exchange''s database';
CREATE TABLE auditor_historic_denomination_revenue
  (row_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
  ,denom_pub_hash BYTEA PRIMARY KEY CHECK (LENGTH(denom_pub_hash)=64)
  ,revenue_timestamp BIGINT NOT NULL
  ,revenue_balance taler_amount NOT NULL
  ,loss_balance taler_amount NOT NULL
  );
COMMENT ON TABLE auditor_historic_denomination_revenue
  IS 'Table with historic profits; basically, when a denom_pub has expired and everything associated with it is garbage collected, the final profits end up in here; note that the denom_pub here is not a foreign key, we just keep it as a reference point.';
COMMENT ON COLUMN auditor_historic_denomination_revenue.denom_pub_hash
  IS 'hash of the denomination public key that created this revenue';
COMMENT ON COLUMN auditor_historic_denomination_revenue.revenue_timestamp
  IS 'when was this revenue realized (by the denomination public key expiring)';
COMMENT ON COLUMN auditor_historic_denomination_revenue.revenue_balance
  IS 'the sum of all of the profits we made on the denomination except for withdraw fees (which are in historic_reserve_revenue); so this includes the deposit, melt and refund fees';
COMMENT ON COLUMN auditor_historic_denomination_revenue.loss_balance
  IS 'the sum of all of the losses we made on the denomination (for example, because the signing key was compromised and thus we redeemed coins we never issued); of course should be zero in practice in most cases';
CREATE TABLE IF NOT EXISTS auditor_historic_reserve_summary
  (
      row_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE,
      start_date BIGINT NOT NULL,
      end_date BIGINT NOT NULL,
      reserve_profits taler_amount NOT NULL,
      PRIMARY KEY (start_date, end_date)
  );
COMMENT ON TABLE auditor_historic_reserve_summary
  IS 'historic profits from reserves; we eventually GC auditor_historic_reserve_revenue, and then store the totals in here (by time intervals).';
COMMENT ON COLUMN auditor_historic_reserve_summary.start_date
  IS 'start date of the time interval over which we made these profits from reserves';
COMMENT ON COLUMN auditor_historic_reserve_summary.end_date
  IS 'end date (exclusive) of the time interval over which we made these profits from reserves';
COMMENT ON COLUMN auditor_historic_reserve_summary.reserve_profits
  IS 'total amount in profits made';
CREATE TABLE IF NOT EXISTS auditor_misattribution_in_inconsistency
(
    row_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE PRIMARY KEY,
    amount taler_amount NOT NULL,
    bank_row BIGINT NOT NULL,
    reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_pub)=32),
    suppressed BOOLEAN NOT NULL DEFAULT FALSE
);
COMMENT ON TABLE auditor_misattribution_in_inconsistency
    IS 'Report wire transfer that was smaller than it should have been.';
CREATE TABLE IF NOT EXISTS auditor_progress
  (progress_key TEXT PRIMARY KEY NOT NULL
  ,progress_offset BIGINT NOT NULL
  );
COMMENT ON TABLE auditor_progress
  IS 'Information about to the point until which the audit has progressed.  Used for SELECTing the statements to process.';
COMMENT ON COLUMN auditor_progress.progress_key
  IS 'Name of the progress indicator';
COMMENT ON COLUMN auditor_progress.progress_offset
  IS 'Table offset or timestamp or counter until which the audit has progressed';
CREATE TABLE IF NOT EXISTS auditor_purse_not_closed_inconsistencies
(
    row_id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    purse_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32),
    amount taler_amount NOT NULL,
    expiration_date BIGINT NOT NULL,
    suppressed BOOLEAN NOT NULL DEFAULT FALSE
);
COMMENT ON TABLE auditor_purse_not_closed_inconsistencies
    IS 'Report expired purses';
CREATE TABLE auditor_purses
  (auditor_purses_rowid BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE NOT NULL
  ,purse_pub BYTEA PRIMARY KEY CHECK(LENGTH(purse_pub)=32) NOT NULL
  ,balance taler_amount NOT NULL DEFAULT(0,0)
  ,target taler_amount NOT NULL
  ,expiration_date BIGINT NOT NULL
  );
COMMENT ON TABLE auditor_purses
  IS 'all of the purses and their respective balances that the auditor is aware of';
CREATE TABLE IF NOT EXISTS auditor_refreshes_hanging
(
    row_id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    problem_row_id BIGINT NOT NULL,
    amount taler_amount NOT NULL,
    suppressed BOOLEAN NOT NULL DEFAULT FALSE
);
COMMENT ON TABLE auditor_refreshes_hanging
    IS 'Report a hanging refresh.';
CREATE TABLE IF NOT EXISTS auditor_reserve_balance_insufficient_inconsistency
(
    row_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE PRIMARY KEY,
    reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_pub)=32),
    inconsistency_gain BOOLEAN NOT NULL,
    inconsistency_amount taler_amount NOT NULL,
    suppressed BOOLEAN NOT NULL DEFAULT FALSE
);
COMMENT ON TABLE auditor_reserve_balance_insufficient_inconsistency
    IS 'Report a (serious) balance insufficiency in the exchange''s database';
CREATE TABLE IF NOT EXISTS auditor_reserve_balance_summary_wrong_inconsistency
(
    row_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE PRIMARY KEY,
    reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_pub)=32),
    exchange_amount taler_amount NOT NULL,
    auditor_amount taler_amount NOT NULL,
    suppressed BOOLEAN NOT NULL DEFAULT FALSE
);
COMMENT ON TABLE auditor_reserve_balance_summary_wrong_inconsistency
    IS 'Report a (serious) reserve balance insufficiency.';
SET search_path TO auditor;
CREATE TABLE IF NOT EXISTS auditor_reserve_in_inconsistency
(
    row_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE PRIMARY KEY,
    bank_row_id INT8 NOT NULL,
    amount_exchange_expected taler_amount NOT NULL,
    amount_wired taler_amount NOT NULL,
    reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_pub)=32),
    timestamp BIGINT NOT NULL,
    account TEXT NOT NULL,
    diagnostic TEXT NOT NULL,
    suppressed BOOLEAN NOT NULL DEFAULT FALSE
);
COMMENT ON TABLE auditor_reserve_in_inconsistency
    IS 'Report an incoming wire transfer claimed by exchange not found.';
CREATE TABLE IF NOT EXISTS auditor_reserve_not_closed_inconsistency
(
    row_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE ,
    reserve_pub BYTEA PRIMARY KEY NOT NULL CHECK (LENGTH(reserve_pub)=32),
    balance taler_amount NOT NULL,
    expiration_time BIGINT,
    diagnostic TEXT,
    suppressed BOOLEAN NOT NULL DEFAULT FALSE
);
COMMENT ON TABLE auditor_reserve_not_closed_inconsistency
    IS 'Report a (serious) reserve balance insufficiency.';
CREATE TABLE auditor_reserves
  (auditor_reserves_rowid BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE
  ,reserve_pub BYTEA PRIMARY KEY CHECK(LENGTH(reserve_pub)=32)
  ,reserve_balance taler_amount NOT NULL
  ,reserve_loss taler_amount NOT NULL
  ,withdraw_fee_balance taler_amount NOT NULL
  ,close_fee_balance taler_amount NOT NULL
  ,purse_fee_balance taler_amount NOT NULL
  ,open_fee_balance taler_amount NOT NULL
  ,history_fee_balance taler_amount NOT NULL
  ,expiration_date BIGINT NOT NULL
  ,origin_account TEXT
  );
COMMENT ON TABLE auditor_reserves
  IS 'all of the customer reserves and their respective balances that the auditor is aware of';
CREATE TABLE IF NOT EXISTS auditor_row_inconsistency
(
    row_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE PRIMARY KEY,
    row_table TEXT NOT NULL,
    diagnostic TEXT NOT NULL,
    problem_row_id INT8 NOT NULL,
    suppressed BOOLEAN NOT NULL DEFAULT FALSE
);
COMMENT ON TABLE auditor_row_inconsistency
    IS 'Report a (serious) row inconsistency in the exchange''s database';
CREATE TABLE IF NOT EXISTS auditor_row_minor_inconsistencies
(
    row_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE PRIMARY KEY,
    row_table TEXT NOT NULL,
    problem_row BIGINT NOT NULL,
    diagnostic TEXT NOT NULL,
    suppressed BOOLEAN NOT NULL DEFAULT FALSE
);
COMMENT ON TABLE auditor_row_minor_inconsistencies
    IS 'Report a (minor) row inconsistency in the exchange''s database.';
CREATE TABLE IF NOT EXISTS auditor_wire_format_inconsistency
(
    row_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE PRIMARY KEY,
    amount taler_amount NOT NULL,
    wire_offset BIGINT NOT NULL,
    diagnostic TEXT NOT NULL,
    suppressed BOOLEAN NOT NULL DEFAULT FALSE
);
COMMENT ON TABLE auditor_wire_format_inconsistency
    IS 'Report a (serious) format inconsistency.';
CREATE TABLE IF NOT EXISTS auditor_wire_out_inconsistency
(
    row_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE PRIMARY KEY,
    destination_account TEXT NOT NULL,
    diagnostic TEXT NOT NULL,
    wire_out_serial_id INT8 NOT NULL,
    expected taler_amount NOT NULL,
    claimed taler_amount NOT NULL,
    suppressed BOOLEAN NOT NULL DEFAULT FALSE
);
COMMENT ON TABLE auditor_wire_out_inconsistency
    IS 'Report a (serious) wire inconsistency in the exchange''s database';
CREATE TABLE IF NOT EXISTS auditor_pending_deposits
(
    row_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE PRIMARY KEY,
    total_amount taler_amount NOT NULL,
    wire_target_h_payto BYTEA NOT NULL CHECK (LENGTH(wire_target_h_payto)=32),
    batch_deposit_serial_id BIGINT NOT NULL,
    deadline BIGINT NOT NULL,
    suppressed BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE TABLE IF NOT EXISTS auditor_early_aggregations
(
    row_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE PRIMARY KEY,
    batch_deposit_serial_id BIGINT UNIQUE NOT NULL,
    tracking_serial_id BIGINT UNIQUE NOT NULL,
    amount taler_amount NOT NULL,
    suppressed BOOLEAN NOT NULL DEFAULT FALSE
);
COMMENT ON TABLE auditor_early_aggregations
  IS 'Reports when aggregations/transfers are encountered before their justifications (can be harmless, if the justifications appear shortly afterwards).';
COMMIT;
