chio-store-sqlite 0.1.2

SQLite-backed persistence, query, and report implementations for Chio
Documentation
use super::*;

fn canonical_approval_digest(value: &str) -> bool {
    value.len() == 64
        && value
            .bytes()
            .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))
}

fn validate_cumulative_approval_history(
    transaction: &Transaction<'_>,
    request: &BudgetCumulativeApprovalRequest,
    state: BudgetCumulativeApprovalState,
    approval_set_digest: Option<&str>,
) -> Result<(), BudgetStoreError> {
    type InitialRow = (
        String,
        Option<String>,
        Option<String>,
        String,
        i64,
        i64,
        i64,
        i64,
    );
    let initial: InitialRow = transaction
        .query_row(
            r#"
            SELECT event.kind, event.authorization_outcome,
                   cumulative.state_before, cumulative.state_after,
                   cumulative.reserved_authorized_before,
                   cumulative.captured_authorized_before,
                   cumulative.requested_authorized_units,
                   cumulative.effective_threshold_units
            FROM budget_event_cumulative_approval AS cumulative
            JOIN budget_mutation_events AS event
              ON event.event_id = cumulative.event_id
            WHERE cumulative.operation_id = ?1
            ORDER BY event.event_seq
            LIMIT 1
            "#,
            params![&request.operation_id],
            |row| {
                Ok((
                    row.get(0)?,
                    row.get(1)?,
                    row.get(2)?,
                    row.get(3)?,
                    row.get(4)?,
                    row.get(5)?,
                    row.get(6)?,
                    row.get(7)?,
                ))
            },
        )
        .map_err(|error| {
            BudgetStoreError::Invariant(format!(
                "cumulative approval operation `{}` lost its reservation history: {error}",
                request.operation_id
            ))
        })?;
    let units = |value: i64, field: &str| {
        u64::try_from(value).map_err(|_| {
            BudgetStoreError::Invariant(format!(
                "cumulative approval operation `{}` has negative {field}",
                request.operation_id
            ))
        })
    };
    let reserved_before = units(initial.4, "reserved units")?;
    let captured_before = units(initial.5, "captured units")?;
    let requested_authorized = units(initial.6, "requested units")?;
    let effective_threshold = units(initial.7, "effective threshold")?;
    let prospective = reserved_before
        .checked_add(captured_before)
        .and_then(|value| value.checked_add(requested_authorized))
        .ok_or_else(|| {
            BudgetStoreError::Invariant(format!(
                "cumulative approval operation `{}` overflows its reservation history",
                request.operation_id
            ))
        })?;
    let approval_required = prospective >= effective_threshold;
    let expected_initial_state = if approval_required {
        "pending_approval"
    } else {
        "authorized"
    };
    if initial.0 != "reserve_invocation"
        || initial.1.as_deref()
            != Some(if approval_required {
                "approval_required"
            } else {
                "authorized"
            })
        || initial.2.is_some()
        || initial.3 != expected_initial_state
        || requested_authorized != request.requested_authorized.units
        || effective_threshold != request.effective_threshold.units
    {
        return Err(BudgetStoreError::Invariant(format!(
            "cumulative approval operation `{}` has invalid reservation history",
            request.operation_id
        )));
    }

    let approval_digests = {
        let mut statement = transaction.prepare(
            r#"
            SELECT event.cumulative_approval_set_digest
            FROM budget_event_cumulative_approval AS cumulative
            JOIN budget_mutation_events AS event
              ON event.event_id = cumulative.event_id
            WHERE cumulative.operation_id = ?1
              AND event.kind = 'authorize_cumulative_approval'
            ORDER BY event.event_seq
            "#,
        )?;
        let rows = statement
            .query_map(params![&request.operation_id], |row| {
                row.get::<_, Option<String>>(0)
            })?
            .collect::<Result<Vec<_>, _>>()?;
        rows
    };
    let exact_approval = match approval_set_digest {
        Some(digest) if canonical_approval_digest(digest) => {
            approval_digests.as_slice() == [Some(digest.to_string())]
        }
        Some(_) => false,
        None => approval_digests.is_empty(),
    };
    let valid_state = match state {
        BudgetCumulativeApprovalState::PendingApproval => {
            approval_required && approval_set_digest.is_none()
        }
        BudgetCumulativeApprovalState::Authorized | BudgetCumulativeApprovalState::Captured => {
            approval_required == approval_set_digest.is_some()
        }
        BudgetCumulativeApprovalState::ReversedBeforeDispatch => {
            approval_required || approval_set_digest.is_none()
        }
    };
    if !exact_approval || !valid_state {
        return Err(BudgetStoreError::Invariant(format!(
            "cumulative approval operation `{}` has invalid approval history",
            request.operation_id
        )));
    }
    Ok(())
}

pub(super) fn load_hold_cumulative(
    transaction: &Transaction<'_>,
    hold_id: &str,
) -> Result<
    Option<(
        BudgetCumulativeApprovalRequest,
        BudgetCumulativeApprovalState,
        Option<String>,
    )>,
    BudgetStoreError,
> {
    type Row = (
        String,
        String,
        String,
        i64,
        i64,
        i64,
        String,
        Option<String>,
        String,
        Option<String>,
        Option<String>,
        String,
        i64,
    );
    let row: Option<Row> = transaction
        .query_row(
            r#"
            SELECT operation.operation_id, operation.authority_id,
                   operation.owner_id, operation.approval_budget_epoch,
                   operation.effective_threshold_units,
                   operation.requested_authorized_units, operation.state,
                   operation.approval_set_digest, operation.approval_budget_id,
                   account.delegation_root_id, account.root_binding_digest,
                   account.root_grant_hash, account.authority_threshold_units
            FROM budget_cumulative_approval_operations AS operation
            JOIN budget_cumulative_approval_accounts AS account
              ON account.authority_id = operation.authority_id
             AND account.owner_id = operation.owner_id
             AND account.approval_budget_id = operation.approval_budget_id
             AND account.approval_budget_epoch = operation.approval_budget_epoch
            WHERE operation.hold_id = ?1
            "#,
            params![hold_id],
            |row| {
                Ok((
                    row.get(0)?,
                    row.get(1)?,
                    row.get(2)?,
                    row.get(3)?,
                    row.get(4)?,
                    row.get(5)?,
                    row.get(6)?,
                    row.get(7)?,
                    row.get(8)?,
                    row.get(9)?,
                    row.get(10)?,
                    row.get(11)?,
                    row.get(12)?,
                ))
            },
        )
        .optional()?;
    let Some(row) = row else {
        return Ok(None);
    };
    let currency: String = transaction.query_row(
        r#"
        SELECT account.currency
        FROM budget_cumulative_approval_operations AS operation
        JOIN budget_cumulative_approval_accounts AS account
          ON account.authority_id = operation.authority_id
         AND account.owner_id = operation.owner_id
         AND account.approval_budget_id = operation.approval_budget_id
         AND account.approval_budget_epoch = operation.approval_budget_epoch
        WHERE operation.hold_id = ?1
        "#,
        params![hold_id],
        |row| row.get(0),
    )?;
    let units = |value: i64| {
        u64::try_from(value).map_err(|_| {
            BudgetStoreError::Invariant("negative cumulative approval units".to_string())
        })
    };
    let request = BudgetCumulativeApprovalRequest {
        operation_id: row.0,
        account_key: BudgetCumulativeApprovalAccountKey {
            authority_id: row.1,
            owner_id: row.2,
            approval_budget_id: row.8,
            approval_budget_epoch: units(row.3)?,
            root_grant_hash: row.11,
            delegation_root_id: row.9,
            root_binding_digest: row.10,
            currency: currency.clone(),
        },
        authority_threshold: MonetaryAmount {
            units: units(row.12)?,
            currency: currency.clone(),
        },
        effective_threshold: MonetaryAmount {
            units: units(row.4)?,
            currency: currency.clone(),
        },
        requested_authorized: MonetaryAmount {
            units: units(row.5)?,
            currency,
        },
    };
    validate_cumulative_request(&request)?;
    let state = cumulative_state(&row.6)?;
    validate_cumulative_approval_history(transaction, &request, state, row.7.as_deref())?;
    Ok(Some((request, state, row.7)))
}