use rusqlite::{Connection, params};
use zcash_client_backend::data_api::wallet::ConfirmationsPolicy;
use zcash_protocol::consensus::BlockHeight;
use crate::{AccountUuid, error::SqliteClientError};
pub(crate) const PROVISIONAL: u8 = 0;
pub(crate) const ACCEPTED: u8 = 1;
pub(crate) const REJECTED: u8 = 2;
pub(crate) fn truncate(
conn: &rusqlite::Transaction<'_>,
height: BlockHeight,
) -> Result<(), SqliteClientError> {
let confirmed_below = u32::from(height)
.saturating_add(1)
.saturating_sub(ConfirmationsPolicy::default().untrusted().get());
conn.execute(
"UPDATE ironwood_one_time_keys SET closed_at = NULL
WHERE closed_at IS NOT NULL AND active_from IS NOT NULL AND id IN (
SELECT n.one_time_key_id FROM ironwood_received_notes n
JOIN transactions t ON t.id_tx = n.transaction_id
WHERE t.mined_height > ?1)",
[confirmed_below],
)?;
let note_above = "SELECT MIN(t.mined_height) FROM ironwood_received_notes n
JOIN transactions t ON t.id_tx = n.transaction_id
WHERE n.one_time_key_id = ironwood_one_time_keys.id AND t.mined_height > ?1";
conn.execute(
&format!(
"UPDATE ironwood_one_time_keys SET closed_at = NULL, active_from = ({note_above})
WHERE closed_at IS NOT NULL AND active_from IS NULL
AND ({note_above}) IS NOT NULL"
),
[u32::from(height)],
)?;
let floor = u32::from(height);
conn.execute(
"UPDATE ironwood_one_time_publications SET state = ?2
WHERE state IN (?3, ?4) AND height > ?1",
params![floor, PROVISIONAL, ACCEPTED, REJECTED],
)?;
conn.execute(
"UPDATE ironwood_one_time_payment_recovery
SET scan_hint = 1, witness = NULL, witness_height = NULL, witness_hash = NULL
WHERE height > ?1",
[floor],
)?;
conn.execute(
"UPDATE ironwood_one_time_payment_recovery
SET witness = NULL, witness_height = NULL, witness_hash = NULL
WHERE witness_height > ?1",
[floor],
)?;
restart_cursor(
conn,
"SELECT one_time_key_id FROM ironwood_one_time_lookups WHERE last_height > ?1",
floor,
)?;
conn.execute(
"DELETE FROM ironwood_spend_journal WHERE block_height > ?1",
[floor],
)?;
conn.execute(
"DELETE FROM ironwood_journal_coverage WHERE start_height > ?1",
[floor],
)?;
conn.execute(
"UPDATE ironwood_journal_coverage SET end_height = ?1 + 1 WHERE end_height > ?1 + 1",
[floor],
)?;
Ok(())
}
pub(crate) fn reopen_discovery(
conn: &rusqlite::Transaction<'_>,
account: AccountUuid,
) -> Result<(), SqliteClientError> {
let owner = "(SELECT id FROM accounts WHERE uuid = ?1)";
conn.execute(
&format!("DELETE FROM ironwood_one_time_discovery WHERE account_id = {owner}"),
[account.0],
)?;
conn.execute(
&format!("DELETE FROM ironwood_one_time_fresh_accounts WHERE account_id = {owner}"),
[account.0],
)?;
let keys = format!("SELECT id FROM ironwood_one_time_keys WHERE account_id = {owner}");
restart_cursor(conn, &keys, account.0)?;
conn.execute(
&format!(
"UPDATE ironwood_one_time_lookups SET cutoff_height = NULL, done_height = NULL
WHERE one_time_key_id IN ({keys})"
),
[account.0],
)?;
Ok(())
}
pub(crate) fn restart_cursor(
conn: &Connection,
keys: &str,
param: impl rusqlite::ToSql,
) -> Result<(), SqliteClientError> {
conn.execute(
&format!(
"UPDATE ironwood_one_time_lookups
SET applied = 0, last_position = NULL, last_nullifier = NULL, last_height = NULL,
cutoff_height = COALESCE(cutoff_height, done_height), done_height = NULL
WHERE one_time_key_id IN ({keys})"
),
[¶m],
)?;
conn.execute(
&format!("DELETE FROM ironwood_one_time_lookup_reads WHERE one_time_key_id IN ({keys})"),
[¶m],
)?;
conn.execute(
&format!(
"DELETE FROM ironwood_one_time_payment_recovery WHERE one_time_key_id IN ({keys})"
),
[¶m],
)?;
Ok(())
}