fix: bind deletion approval to frozen inventory digest

Co-authored-by: npub1dccv64krpcpse5cmkzfeh998cftungyatw3djt8jwdw6g43f7fyqzzmrf7 <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@buzz.block.builderlab.xyz>
Signed-off-by: npub1dccv64krpcpse5cmkzfeh998cftungyatw3djt8jwdw6g43f7fyqzzmrf7 <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@buzz.block.builderlab.xyz>
This commit is contained in:
npub1dccv64krpcpse5cmkzfeh998cftungyatw3djt8jwdw6g43f7fyqzzmrf7
2026-08-02 15:13:11 -07:00
parent e00dee77e2
commit 79536ff35f
4 changed files with 49 additions and 7 deletions
+1 -1
View File
@@ -689,7 +689,7 @@ jobs:
run: |
cargo nextest run \
--archive-file target/ci/backend-integration-tests.tar.zst \
-E '(package(buzz-deletion) and test(/tests::/)) or (package(buzz-relay) and test(/finalize_push_(holds_serving_lease_through_post_cas_publication|db_failure_after_cas_is_not_success_and_releases_lease)/))' \
-E '(package(buzz-deletion) and test(/tests::/)) or (package(buzz-db) and test(/deletion::postgres_tests/)) or (package(buzz-relay) and test(/finalize_push_(holds_serving_lease_through_post_cas_publication|db_failure_after_cas_is_not_success_and_releases_lease)/))' \
--run-ignored ignored-only
env:
DATABASE_URL: postgres://buzz:${{ env.BUZZ_TEST_POSTGRES_PASSWORD }}@localhost:5432/buzz
+34
View File
@@ -2440,6 +2440,18 @@ mod postgres_tests {
.expect("live schema after row churn");
assert_eq!(current_schema, inventory.schema);
let mismatched_insert = sqlx::query(
"INSERT INTO community_deletion_approvals \
(request_id, inventory_digest, approved_by) VALUES ($1, $2, 'tampered')",
)
.bind(request.id)
.bind(vec![0_u8; 32])
.execute(&db.pool)
.await;
assert!(
mismatched_insert.is_err(),
"a mismatched approval must be unrepresentable"
);
let approved = store
.approve(request.id, "approver-a", Some("reviewed"))
.await
@@ -2449,6 +2461,28 @@ mod postgres_tests {
approved.inventory_digest,
Some(hex::encode(inventory.digest().unwrap()))
);
let mismatched_approval = sqlx::query(
"UPDATE community_deletion_approvals SET inventory_digest = $2 WHERE request_id = $1",
)
.bind(request.id)
.bind(vec![0_u8; 32])
.execute(&db.pool)
.await;
assert!(
mismatched_approval.is_err(),
"approval digest must remain database-bound to the frozen request digest"
);
let mismatched_request = sqlx::query(
"UPDATE community_deletion_requests SET inventory_digest = $2 WHERE id = $1",
)
.bind(request.id)
.bind(vec![1_u8; 32])
.execute(&db.pool)
.await;
assert!(
mismatched_request.is_err(),
"the frozen request digest must remain bound to its approval"
);
assert!(store
.claim_specific(request.id, "executor-a", DEFAULT_LEASE_DURATION)
.await
+7 -3
View File
@@ -49,7 +49,8 @@ CREATE TABLE community_deletion_requests (
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
completed_at TIMESTAMPTZ,
CHECK ((blocked_at IS NULL) = (blocked_reason IS NULL)),
CHECK ((inventory_frozen_at IS NULL) = (inventory_digest IS NULL))
CHECK ((inventory_frozen_at IS NULL) = (inventory_digest IS NULL)),
UNIQUE (id, inventory_digest)
);
CREATE INDEX community_deletion_requests_runnable
ON community_deletion_requests (next_attempt_at, created_at)
@@ -61,11 +62,14 @@ CREATE INDEX community_deletion_requests_lease
WHERE lease_owner IS NOT NULL;
CREATE TABLE community_deletion_approvals (
request_id UUID PRIMARY KEY REFERENCES community_deletion_requests(id) ON DELETE RESTRICT,
request_id UUID PRIMARY KEY,
inventory_digest BYTEA NOT NULL CHECK (length(inventory_digest) = 32),
approved_by TEXT NOT NULL,
note TEXT,
approved_at TIMESTAMPTZ NOT NULL DEFAULT now()
approved_at TIMESTAMPTZ NOT NULL DEFAULT now(),
FOREIGN KEY (request_id, inventory_digest)
REFERENCES community_deletion_requests(id, inventory_digest)
ON DELETE RESTRICT
);
CREATE TABLE community_deletion_checkpoints (
+7 -3
View File
@@ -1112,7 +1112,8 @@ CREATE TABLE community_deletion_requests (
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
completed_at TIMESTAMPTZ,
CHECK ((blocked_at IS NULL) = (blocked_reason IS NULL)),
CHECK ((inventory_frozen_at IS NULL) = (inventory_digest IS NULL))
CHECK ((inventory_frozen_at IS NULL) = (inventory_digest IS NULL)),
UNIQUE (id, inventory_digest)
);
CREATE INDEX community_deletion_requests_runnable
ON community_deletion_requests (next_attempt_at, created_at)
@@ -1123,11 +1124,14 @@ CREATE INDEX community_deletion_requests_lease
ON community_deletion_requests (lease_until) WHERE lease_owner IS NOT NULL;
CREATE TABLE community_deletion_approvals (
request_id UUID PRIMARY KEY REFERENCES community_deletion_requests(id) ON DELETE RESTRICT,
request_id UUID PRIMARY KEY,
inventory_digest BYTEA NOT NULL CHECK (length(inventory_digest) = 32),
approved_by TEXT NOT NULL,
note TEXT,
approved_at TIMESTAMPTZ NOT NULL DEFAULT now()
approved_at TIMESTAMPTZ NOT NULL DEFAULT now(),
FOREIGN KEY (request_id, inventory_digest)
REFERENCES community_deletion_requests(id, inventory_digest)
ON DELETE RESTRICT
);
CREATE TABLE community_deletion_checkpoints (
request_id UUID NOT NULL REFERENCES community_deletion_requests(id) ON DELETE RESTRICT,