chio-store-sqlite 0.1.2

SQLite-backed persistence, query, and report implementations for Chio
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
pub(super) const FROST_STORE_SCHEMA: &str = r#"
CREATE TABLE IF NOT EXISTS frost_ceremonies (
    ceremony_id TEXT PRIMARY KEY CHECK (
        length(ceremony_id) = 64
        AND ceremony_id NOT GLOB '*[^0-9a-f]*'
    ),
    config_json BLOB NOT NULL CHECK (length(config_json) > 0),
    config_digest TEXT NOT NULL CHECK (
        length(config_digest) = 64
        AND config_digest NOT GLOB '*[^0-9a-f]*'
    ),
    participant_set_digest TEXT NOT NULL CHECK (
        length(participant_set_digest) = 64
        AND participant_set_digest NOT GLOB '*[^0-9a-f]*'
    ),
    scope_id TEXT NOT NULL CHECK (scope_id <> ''),
    key_epoch INTEGER NOT NULL CHECK (key_epoch > 0),
    local_participant_id TEXT NOT NULL CHECK (local_participant_id <> ''),
    state TEXT NOT NULL CHECK (
        state IN ('round1_ready', 'round2_ready', 'completed')
    ),
    state_version INTEGER NOT NULL CHECK (state_version > 0),
    custody_generation TEXT NOT NULL CHECK (custody_generation <> ''),
    secret_kind TEXT NOT NULL CHECK (
        secret_kind IN ('round1', 'round2', 'key_package')
    ),
    secret_nonce BLOB NOT NULL CHECK (length(secret_nonce) = 12),
    secret_ciphertext BLOB NOT NULL CHECK (length(secret_ciphertext) > 16),
    output_nonce BLOB CHECK (output_nonce IS NULL OR length(output_nonce) = 12),
    output_ciphertext BLOB,
    input_transcript_digest TEXT CHECK (
        input_transcript_digest IS NULL
        OR (
            length(input_transcript_digest) = 64
            AND input_transcript_digest NOT GLOB '*[^0-9a-f]*'
        )
    ),
    public_key_package BLOB,
    group_public_key TEXT CHECK (
        group_public_key IS NULL
        OR (
            length(group_public_key) = 64
            AND group_public_key NOT GLOB '*[^0-9a-f]*'
        )
    ),
    verification_shares_json BLOB,
    source_store_uuid TEXT NOT NULL CHECK (source_store_uuid <> ''),
    source_lease_id TEXT NOT NULL CHECK (source_lease_id <> ''),
    source_owner_epoch INTEGER NOT NULL CHECK (source_owner_epoch > 0),
    updated_at_unix_ms INTEGER NOT NULL CHECK (updated_at_unix_ms > 0),
    completed_at_unix_ms INTEGER CHECK (completed_at_unix_ms > 0),
    record_digest TEXT NOT NULL CHECK (
        length(record_digest) = 64
        AND record_digest NOT GLOB '*[^0-9a-f]*'
    ),
    CHECK (
        (state = 'round1_ready' AND state_version = 1
         AND secret_kind = 'round1'
         AND output_nonce IS NOT NULL AND output_ciphertext IS NOT NULL
         AND input_transcript_digest IS NULL
         AND public_key_package IS NULL AND group_public_key IS NULL
         AND verification_shares_json IS NULL AND completed_at_unix_ms IS NULL)
        OR
        (state = 'round2_ready' AND state_version = 2
         AND secret_kind = 'round2'
         AND output_nonce IS NOT NULL AND output_ciphertext IS NOT NULL
         AND input_transcript_digest IS NOT NULL
         AND public_key_package IS NULL AND group_public_key IS NULL
         AND verification_shares_json IS NULL AND completed_at_unix_ms IS NULL)
        OR
        (state = 'completed' AND state_version = 3
         AND secret_kind = 'key_package'
         AND output_nonce IS NULL AND output_ciphertext IS NULL
         AND input_transcript_digest IS NOT NULL
         AND public_key_package IS NOT NULL AND group_public_key IS NOT NULL
         AND verification_shares_json IS NOT NULL
         AND completed_at_unix_ms IS NOT NULL)
    )
);

CREATE INDEX IF NOT EXISTS frost_ceremonies_scope_epoch
ON frost_ceremonies(scope_id, key_epoch, state);

CREATE UNIQUE INDEX IF NOT EXISTS frost_ceremonies_scope_epoch_participant
ON frost_ceremonies(scope_id, key_epoch, local_participant_id);

CREATE TABLE IF NOT EXISTS frost_roster_history (
    scope_id TEXT NOT NULL CHECK (scope_id <> ''),
    key_epoch INTEGER NOT NULL CHECK (key_epoch > 0),
    roster_id TEXT NOT NULL CHECK (
        length(roster_id) = 64 AND roster_id NOT GLOB '*[^0-9a-f]*'
    ),
    roster_digest TEXT NOT NULL CHECK (
        length(roster_digest) = 64 AND roster_digest NOT GLOB '*[^0-9a-f]*'
    ),
    roster_json BLOB NOT NULL CHECK (length(roster_json) > 0),
    checkpoint_sequence INTEGER NOT NULL CHECK (checkpoint_sequence > 0),
    checkpoint_digest TEXT NOT NULL CHECK (
        length(checkpoint_digest) = 64 AND checkpoint_digest NOT GLOB '*[^0-9a-f]*'
    ),
    checkpoint_json BLOB NOT NULL CHECK (length(checkpoint_json) > 0),
    activation_fence INTEGER NOT NULL CHECK (activation_fence > 0),
    clock_high_water INTEGER NOT NULL CHECK (clock_high_water > 0),
    activated_at_unix_ms INTEGER NOT NULL CHECK (activated_at_unix_ms > 0),
    source_store_uuid TEXT NOT NULL CHECK (source_store_uuid <> ''),
    source_lease_id TEXT NOT NULL CHECK (source_lease_id <> ''),
    source_owner_epoch INTEGER NOT NULL CHECK (source_owner_epoch > 0),
    record_digest TEXT NOT NULL CHECK (
        length(record_digest) = 64 AND record_digest NOT GLOB '*[^0-9a-f]*'
    ),
    PRIMARY KEY (scope_id, key_epoch),
    UNIQUE (roster_id),
    UNIQUE (roster_digest),
    UNIQUE (checkpoint_digest)
);

CREATE TABLE IF NOT EXISTS frost_active_rosters (
    scope_id TEXT PRIMARY KEY CHECK (scope_id <> ''),
    key_epoch INTEGER NOT NULL CHECK (key_epoch > 0),
    roster_digest TEXT NOT NULL CHECK (
        length(roster_digest) = 64 AND roster_digest NOT GLOB '*[^0-9a-f]*'
    ),
    checkpoint_sequence INTEGER NOT NULL CHECK (checkpoint_sequence > 0),
    checkpoint_digest TEXT NOT NULL CHECK (
        length(checkpoint_digest) = 64 AND checkpoint_digest NOT GLOB '*[^0-9a-f]*'
    ),
    activation_fence INTEGER NOT NULL CHECK (activation_fence > 0),
    clock_high_water INTEGER NOT NULL CHECK (clock_high_water > 0),
    source_store_uuid TEXT NOT NULL CHECK (source_store_uuid <> ''),
    source_lease_id TEXT NOT NULL CHECK (source_lease_id <> ''),
    source_owner_epoch INTEGER NOT NULL CHECK (source_owner_epoch > 0),
    FOREIGN KEY (scope_id, key_epoch)
        REFERENCES frost_roster_history(scope_id, key_epoch),
    FOREIGN KEY (roster_digest)
        REFERENCES frost_roster_history(roster_digest),
    FOREIGN KEY (checkpoint_digest)
        REFERENCES frost_roster_history(checkpoint_digest)
);

CREATE TABLE IF NOT EXISTS frost_roster_rotations (
    rotation_id TEXT PRIMARY KEY CHECK (
        length(rotation_id) = 64 AND rotation_id NOT GLOB '*[^0-9a-f]*'
    ),
    scope_id TEXT NOT NULL CHECK (scope_id <> ''),
    state TEXT NOT NULL CHECK (
        state IN ('staged', 'anchor_advanced', 'active', 'discarded')
    ),
    state_version INTEGER NOT NULL CHECK (state_version BETWEEN 1 AND 3),
    predecessor_checkpoint_digest TEXT NOT NULL CHECK (
        length(predecessor_checkpoint_digest) = 64
        AND predecessor_checkpoint_digest NOT GLOB '*[^0-9a-f]*'
    ),
    predecessor_checkpoint_json BLOB NOT NULL CHECK (
        length(predecessor_checkpoint_json) > 0
    ),
    target_roster_digest TEXT NOT NULL CHECK (
        length(target_roster_digest) = 64
        AND target_roster_digest NOT GLOB '*[^0-9a-f]*'
    ),
    target_roster_json BLOB NOT NULL CHECK (length(target_roster_json) > 0),
    target_key_epoch INTEGER NOT NULL CHECK (target_key_epoch > 1),
    rotation_authorization_digest TEXT NOT NULL CHECK (
        length(rotation_authorization_digest) = 64
        AND rotation_authorization_digest NOT GLOB '*[^0-9a-f]*'
    ),
    burn_summary_json BLOB NOT NULL CHECK (length(burn_summary_json) > 0),
    old_session_burn_root TEXT NOT NULL CHECK (
        length(old_session_burn_root) = 64
        AND old_session_burn_root NOT GLOB '*[^0-9a-f]*'
    ),
    expected_checkpoint_sequence INTEGER NOT NULL CHECK (
        expected_checkpoint_sequence > 1
    ),
    activation_fence INTEGER NOT NULL CHECK (activation_fence > 0),
    clock_high_water INTEGER NOT NULL CHECK (clock_high_water > 0),
    anchored_checkpoint_digest TEXT CHECK (
        anchored_checkpoint_digest IS NULL
        OR (
            length(anchored_checkpoint_digest) = 64
            AND anchored_checkpoint_digest NOT GLOB '*[^0-9a-f]*'
        )
    ),
    anchored_checkpoint_json BLOB,
    source_store_uuid TEXT NOT NULL CHECK (source_store_uuid <> ''),
    source_lease_id TEXT NOT NULL CHECK (source_lease_id <> ''),
    source_owner_epoch INTEGER NOT NULL CHECK (source_owner_epoch > 0),
    created_at_unix_ms INTEGER NOT NULL CHECK (created_at_unix_ms > 0),
    updated_at_unix_ms INTEGER NOT NULL CHECK (
        updated_at_unix_ms >= created_at_unix_ms
    ),
    record_digest TEXT NOT NULL CHECK (
        length(record_digest) = 64 AND record_digest NOT GLOB '*[^0-9a-f]*'
    ),
    CHECK (
        (state = 'staged' AND state_version = 1
         AND anchored_checkpoint_digest IS NULL AND anchored_checkpoint_json IS NULL)
        OR
        (state IN ('anchor_advanced', 'discarded') AND state_version = 2
         AND (
             (state = 'anchor_advanced' AND anchored_checkpoint_digest IS NOT NULL
              AND anchored_checkpoint_json IS NOT NULL)
             OR
             (state = 'discarded' AND anchored_checkpoint_digest IS NULL
              AND anchored_checkpoint_json IS NULL)
         ))
        OR
        (state = 'active' AND state_version = 3
         AND anchored_checkpoint_digest IS NOT NULL
         AND anchored_checkpoint_json IS NOT NULL)
    )
);

CREATE UNIQUE INDEX IF NOT EXISTS frost_roster_rotations_target
ON frost_roster_rotations(scope_id, target_key_epoch);

CREATE UNIQUE INDEX IF NOT EXISTS frost_roster_rotations_live_scope
ON frost_roster_rotations(scope_id)
WHERE state IN ('staged', 'anchor_advanced');

CREATE TABLE IF NOT EXISTS frost_signer_sessions (
    session_id TEXT NOT NULL CHECK (
        length(session_id) = 64 AND session_id NOT GLOB '*[^0-9a-f]*'
    ),
    participant_id TEXT NOT NULL CHECK (participant_id <> ''),
    authorization_slot_id TEXT NOT NULL CHECK (
        length(authorization_slot_id) = 64
        AND authorization_slot_id NOT GLOB '*[^0-9a-f]*'
    ),
    scope_id TEXT NOT NULL CHECK (scope_id <> ''),
    key_epoch INTEGER NOT NULL CHECK (key_epoch > 0),
    authorization_id TEXT NOT NULL CHECK (
        length(authorization_id) = 64
        AND authorization_id NOT GLOB '*[^0-9a-f]*'
    ),
    signing_message_digest TEXT NOT NULL CHECK (
        length(signing_message_digest) = 64
        AND signing_message_digest NOT GLOB '*[^0-9a-f]*'
    ),
    roster_digest TEXT NOT NULL CHECK (
        length(roster_digest) = 64 AND roster_digest NOT GLOB '*[^0-9a-f]*'
    ),
    coordinator_id TEXT NOT NULL CHECK (coordinator_id <> ''),
    resource_fence INTEGER NOT NULL CHECK (resource_fence > 0),
    ceremony_id TEXT NOT NULL CHECK (
        length(ceremony_id) = 64 AND ceremony_id NOT GLOB '*[^0-9a-f]*'
    ),
    authorization_body_json BLOB NOT NULL CHECK (length(authorization_body_json) > 0),
    bound_checkpoint_digest TEXT NOT NULL CHECK (
        length(bound_checkpoint_digest) = 64
        AND bound_checkpoint_digest NOT GLOB '*[^0-9a-f]*'
    ),
    bound_checkpoint_json BLOB NOT NULL CHECK (length(bound_checkpoint_json) > 0),
    signer_identifier BLOB NOT NULL CHECK (length(signer_identifier) > 0),
    verification_share TEXT NOT NULL CHECK (
        length(verification_share) = 64
        AND verification_share NOT GLOB '*[^0-9a-f]*'
    ),
    state TEXT NOT NULL CHECK (
        state IN ('prepared', 'commitment_published', 'share_ready', 'completed', 'burned')
    ),
    state_version INTEGER NOT NULL CHECK (state_version BETWEEN 1 AND 4),
    custody_generation TEXT NOT NULL CHECK (custody_generation <> ''),
    nonce_aad BLOB NOT NULL CHECK (length(nonce_aad) > 0),
    nonce_ciphertext BLOB,
    nonce_nonce BLOB CHECK (nonce_nonce IS NULL OR length(nonce_nonce) = 12),
    commitment_bytes BLOB NOT NULL CHECK (length(commitment_bytes) > 0),
    commitment_digest TEXT NOT NULL CHECK (
        length(commitment_digest) = 64
        AND commitment_digest NOT GLOB '*[^0-9a-f]*'
    ),
    signing_package_digest TEXT CHECK (
        signing_package_digest IS NULL
        OR (length(signing_package_digest) = 64
            AND signing_package_digest NOT GLOB '*[^0-9a-f]*')
    ),
    signature_share BLOB,
    burn_reason TEXT,
    source_store_uuid TEXT NOT NULL CHECK (source_store_uuid <> ''),
    source_lease_id TEXT NOT NULL CHECK (source_lease_id <> ''),
    source_owner_epoch INTEGER NOT NULL CHECK (source_owner_epoch > 0),
    created_at_unix_ms INTEGER NOT NULL CHECK (created_at_unix_ms > 0),
    updated_at_unix_ms INTEGER NOT NULL CHECK (updated_at_unix_ms >= created_at_unix_ms),
    record_digest TEXT NOT NULL CHECK (
        length(record_digest) = 64 AND record_digest NOT GLOB '*[^0-9a-f]*'
    ),
    PRIMARY KEY (session_id, participant_id),
    UNIQUE (authorization_slot_id, participant_id),
    CHECK (
        (state = 'prepared' AND state_version = 1
         AND nonce_ciphertext IS NOT NULL AND nonce_nonce IS NOT NULL
         AND signing_package_digest IS NULL AND signature_share IS NULL
         AND burn_reason IS NULL)
        OR
        (state = 'commitment_published' AND state_version = 2
         AND nonce_ciphertext IS NOT NULL AND nonce_nonce IS NOT NULL
         AND signing_package_digest IS NULL AND signature_share IS NULL
         AND burn_reason IS NULL)
        OR
        (state = 'share_ready' AND state_version = 3
         AND nonce_ciphertext IS NOT NULL AND nonce_nonce IS NOT NULL
         AND signing_package_digest IS NOT NULL AND signature_share IS NOT NULL
         AND burn_reason IS NULL)
        OR
        (state = 'completed' AND state_version = 4
         AND nonce_ciphertext IS NULL AND nonce_nonce IS NULL
         AND signing_package_digest IS NOT NULL AND signature_share IS NOT NULL
         AND burn_reason IS NULL)
        OR
        (state = 'burned' AND state_version BETWEEN 2 AND 4
         AND nonce_ciphertext IS NULL AND nonce_nonce IS NULL
         AND signature_share IS NULL AND burn_reason IS NOT NULL)
    )
);

CREATE TABLE IF NOT EXISTS frost_coordinator_sessions (
    session_id TEXT PRIMARY KEY CHECK (
        length(session_id) = 64 AND session_id NOT GLOB '*[^0-9a-f]*'
    ),
    authorization_slot_id TEXT NOT NULL UNIQUE CHECK (
        length(authorization_slot_id) = 64
        AND authorization_slot_id NOT GLOB '*[^0-9a-f]*'
    ),
    scope_id TEXT NOT NULL CHECK (scope_id <> ''),
    key_epoch INTEGER NOT NULL CHECK (key_epoch > 0),
    authorization_id TEXT NOT NULL CHECK (
        length(authorization_id) = 64
        AND authorization_id NOT GLOB '*[^0-9a-f]*'
    ),
    signing_message_digest TEXT NOT NULL CHECK (
        length(signing_message_digest) = 64
        AND signing_message_digest NOT GLOB '*[^0-9a-f]*'
    ),
    roster_digest TEXT NOT NULL CHECK (
        length(roster_digest) = 64 AND roster_digest NOT GLOB '*[^0-9a-f]*'
    ),
    coordinator_id TEXT NOT NULL CHECK (coordinator_id <> ''),
    resource_fence INTEGER NOT NULL CHECK (resource_fence > 0),
    authorization_body_json BLOB NOT NULL CHECK (length(authorization_body_json) > 0),
    roster_json BLOB NOT NULL CHECK (length(roster_json) > 0),
    bound_checkpoint_digest TEXT NOT NULL CHECK (
        length(bound_checkpoint_digest) = 64
        AND bound_checkpoint_digest NOT GLOB '*[^0-9a-f]*'
    ),
    bound_checkpoint_json BLOB NOT NULL CHECK (length(bound_checkpoint_json) > 0),
    state TEXT NOT NULL CHECK (
        state IN (
            'collecting_commitments', 'package_ready', 'authorization_ready',
            'completed', 'burned'
        )
    ),
    row_version INTEGER NOT NULL CHECK (row_version > 0),
    commitments_json BLOB NOT NULL CHECK (length(commitments_json) > 0),
    signing_participants_json BLOB,
    signing_package BLOB,
    signing_package_digest TEXT CHECK (
        signing_package_digest IS NULL
        OR (length(signing_package_digest) = 64
            AND signing_package_digest NOT GLOB '*[^0-9a-f]*')
    ),
    shares_json BLOB NOT NULL CHECK (length(shares_json) > 0),
    authorization_blob BLOB,
    authorization_blob_digest TEXT CHECK (
        authorization_blob_digest IS NULL
        OR (length(authorization_blob_digest) = 64
            AND authorization_blob_digest NOT GLOB '*[^0-9a-f]*')
    ),
    availability_receipt TEXT,
    burn_reason TEXT,
    coordinator_worker_id TEXT NOT NULL CHECK (coordinator_worker_id <> ''),
    coordinator_lease_id TEXT NOT NULL CHECK (coordinator_lease_id <> ''),
    coordinator_owner_epoch INTEGER NOT NULL CHECK (coordinator_owner_epoch > 0),
    lease_expires_at_unix_ms INTEGER NOT NULL CHECK (lease_expires_at_unix_ms > 0),
    source_store_uuid TEXT NOT NULL CHECK (source_store_uuid <> ''),
    source_lease_id TEXT NOT NULL CHECK (source_lease_id <> ''),
    source_owner_epoch INTEGER NOT NULL CHECK (source_owner_epoch > 0),
    created_at_unix_ms INTEGER NOT NULL CHECK (created_at_unix_ms > 0),
    updated_at_unix_ms INTEGER NOT NULL CHECK (updated_at_unix_ms >= created_at_unix_ms),
    record_digest TEXT NOT NULL CHECK (
        length(record_digest) = 64 AND record_digest NOT GLOB '*[^0-9a-f]*'
    ),
    CHECK (
        (state = 'collecting_commitments'
         AND signing_participants_json IS NULL
         AND signing_package IS NULL AND signing_package_digest IS NULL
         AND authorization_blob IS NULL AND authorization_blob_digest IS NULL
         AND availability_receipt IS NULL AND burn_reason IS NULL)
        OR
        (state = 'package_ready'
         AND signing_participants_json IS NOT NULL
         AND signing_package IS NOT NULL AND signing_package_digest IS NOT NULL
         AND authorization_blob IS NULL AND authorization_blob_digest IS NULL
         AND availability_receipt IS NULL AND burn_reason IS NULL)
        OR
        (state IN ('authorization_ready', 'completed')
         AND signing_participants_json IS NOT NULL
         AND signing_package IS NOT NULL AND signing_package_digest IS NOT NULL
         AND authorization_blob IS NOT NULL AND authorization_blob_digest IS NOT NULL
         AND availability_receipt IS NOT NULL AND burn_reason IS NULL)
        OR
        (state = 'burned' AND burn_reason IS NOT NULL)
    )
);

CREATE TABLE IF NOT EXISTS frost_projection_commits (
    projection_key TEXT NOT NULL CHECK (projection_key <> ''),
    projection_sequence INTEGER NOT NULL CHECK (projection_sequence > 0),
    projection_type TEXT NOT NULL CHECK (
        projection_type IN ('ceremony', 'rotation', 'signer', 'coordinator')
    ),
    mutation_kind TEXT NOT NULL CHECK (mutation_kind <> ''),
    record_digest TEXT NOT NULL CHECK (
        length(record_digest) = 64
        AND record_digest NOT GLOB '*[^0-9a-f]*'
    ),
    previous_chain_digest TEXT NOT NULL CHECK (
        length(previous_chain_digest) = 64
        AND previous_chain_digest NOT GLOB '*[^0-9a-f]*'
    ),
    chain_digest TEXT NOT NULL CHECK (
        length(chain_digest) = 64
        AND chain_digest NOT GLOB '*[^0-9a-f]*'
    ),
    store_uuid TEXT NOT NULL CHECK (store_uuid <> ''),
    store_lease_id TEXT NOT NULL CHECK (store_lease_id <> ''),
    store_owner_epoch INTEGER NOT NULL CHECK (store_owner_epoch > 0),
    PRIMARY KEY (projection_key, projection_sequence)
);

CREATE UNIQUE INDEX IF NOT EXISTS frost_projection_commits_chain
ON frost_projection_commits(chain_digest);

CREATE TRIGGER IF NOT EXISTS frost_ceremonies_no_delete
BEFORE DELETE ON frost_ceremonies
BEGIN
    SELECT RAISE(ABORT, 'FROST ceremony records cannot be deleted');
END;

CREATE TRIGGER IF NOT EXISTS frost_roster_history_immutable
BEFORE UPDATE ON frost_roster_history
BEGIN
    SELECT RAISE(ABORT, 'FROST roster history is immutable');
END;

CREATE TRIGGER IF NOT EXISTS frost_roster_history_no_delete
BEFORE DELETE ON frost_roster_history
BEGIN
    SELECT RAISE(ABORT, 'FROST roster history is immutable');
END;

CREATE TRIGGER IF NOT EXISTS frost_active_rosters_no_delete
BEFORE DELETE ON frost_active_rosters
BEGIN
    SELECT RAISE(ABORT, 'FROST active roster cannot be deleted');
END;

CREATE TRIGGER IF NOT EXISTS frost_active_rosters_monotonic
BEFORE UPDATE ON frost_active_rosters
WHEN NEW.key_epoch <> OLD.key_epoch + 1
  OR NEW.checkpoint_sequence <> OLD.checkpoint_sequence + 1
  OR NEW.activation_fence <= OLD.activation_fence
  OR NEW.clock_high_water < OLD.clock_high_water
BEGIN
    SELECT RAISE(ABORT, 'FROST active roster update is not monotonic');
END;

CREATE TRIGGER IF NOT EXISTS frost_roster_rotations_no_delete
BEFORE DELETE ON frost_roster_rotations
BEGIN
    SELECT RAISE(ABORT, 'FROST rotation records cannot be deleted');
END;

CREATE TRIGGER IF NOT EXISTS frost_roster_rotations_transition
BEFORE UPDATE ON frost_roster_rotations
WHEN NEW.rotation_id <> OLD.rotation_id
  OR NEW.scope_id <> OLD.scope_id
  OR NEW.predecessor_checkpoint_digest <> OLD.predecessor_checkpoint_digest
  OR NEW.predecessor_checkpoint_json <> OLD.predecessor_checkpoint_json
  OR NEW.target_roster_digest <> OLD.target_roster_digest
  OR NEW.target_roster_json <> OLD.target_roster_json
  OR NEW.target_key_epoch <> OLD.target_key_epoch
  OR NEW.rotation_authorization_digest <> OLD.rotation_authorization_digest
  OR NEW.burn_summary_json <> OLD.burn_summary_json
  OR NEW.old_session_burn_root <> OLD.old_session_burn_root
  OR NEW.expected_checkpoint_sequence <> OLD.expected_checkpoint_sequence
  OR NEW.activation_fence <> OLD.activation_fence
  OR NEW.clock_high_water <> OLD.clock_high_water
  OR NEW.source_store_uuid <> OLD.source_store_uuid
  OR NEW.source_owner_epoch < OLD.source_owner_epoch
  OR NEW.created_at_unix_ms <> OLD.created_at_unix_ms
  OR NEW.updated_at_unix_ms < OLD.updated_at_unix_ms
  OR NOT (
      (OLD.state = 'staged' AND NEW.state IN ('anchor_advanced', 'discarded')
       AND NEW.state_version = 2)
      OR
      (OLD.state = 'anchor_advanced' AND NEW.state = 'active'
       AND NEW.state_version = 3
       AND NEW.anchored_checkpoint_digest = OLD.anchored_checkpoint_digest
       AND NEW.anchored_checkpoint_json = OLD.anchored_checkpoint_json)
  )
BEGIN
    SELECT RAISE(ABORT, 'invalid FROST rotation transition');
END;

CREATE TRIGGER IF NOT EXISTS frost_signer_sessions_no_delete
BEFORE DELETE ON frost_signer_sessions
BEGIN
    SELECT RAISE(ABORT, 'FROST signer tombstones cannot be deleted');
END;

CREATE TRIGGER IF NOT EXISTS frost_signer_sessions_transition
BEFORE UPDATE ON frost_signer_sessions
WHEN NEW.session_id <> OLD.session_id
  OR NEW.participant_id <> OLD.participant_id
  OR NEW.authorization_slot_id <> OLD.authorization_slot_id
  OR NEW.scope_id <> OLD.scope_id
  OR NEW.key_epoch <> OLD.key_epoch
  OR NEW.authorization_id <> OLD.authorization_id
  OR NEW.signing_message_digest <> OLD.signing_message_digest
  OR NEW.roster_digest <> OLD.roster_digest
  OR NEW.coordinator_id <> OLD.coordinator_id
  OR NEW.resource_fence <> OLD.resource_fence
  OR NEW.ceremony_id <> OLD.ceremony_id
  OR NEW.authorization_body_json <> OLD.authorization_body_json
  OR NEW.bound_checkpoint_digest <> OLD.bound_checkpoint_digest
  OR NEW.bound_checkpoint_json <> OLD.bound_checkpoint_json
  OR NEW.signer_identifier <> OLD.signer_identifier
  OR NEW.verification_share <> OLD.verification_share
  OR NEW.custody_generation <> OLD.custody_generation
  OR NEW.nonce_aad <> OLD.nonce_aad
  OR NEW.commitment_bytes <> OLD.commitment_bytes
  OR NEW.commitment_digest <> OLD.commitment_digest
  OR NEW.source_store_uuid <> OLD.source_store_uuid
  OR NEW.source_owner_epoch < OLD.source_owner_epoch
  OR NEW.created_at_unix_ms <> OLD.created_at_unix_ms
  OR NEW.updated_at_unix_ms < OLD.updated_at_unix_ms
  OR NOT (
      (OLD.state = 'prepared'
       AND (
           (NEW.state = 'commitment_published' AND NEW.state_version = 2
            AND NEW.nonce_ciphertext = OLD.nonce_ciphertext
            AND NEW.nonce_nonce = OLD.nonce_nonce
            AND NEW.signing_package_digest IS NULL
            AND NEW.signature_share IS NULL
            AND NEW.burn_reason IS NULL)
           OR
           (NEW.state = 'burned' AND NEW.state_version = 2
            AND NEW.nonce_ciphertext IS NULL
            AND NEW.nonce_nonce IS NULL
            AND NEW.signing_package_digest IS NULL
            AND NEW.signature_share IS NULL
            AND NEW.burn_reason IS NOT NULL)
       ))
      OR
      (OLD.state = 'commitment_published'
       AND (
           (NEW.state = 'share_ready' AND NEW.state_version = 3
            AND NEW.nonce_ciphertext = OLD.nonce_ciphertext
            AND NEW.nonce_nonce = OLD.nonce_nonce
            AND NEW.signing_package_digest IS NOT NULL
            AND NEW.signature_share IS NOT NULL
            AND NEW.burn_reason IS NULL)
           OR
           (NEW.state = 'burned' AND NEW.state_version = 3
            AND NEW.nonce_ciphertext IS NULL
            AND NEW.nonce_nonce IS NULL
            AND NEW.signing_package_digest IS NULL
            AND NEW.signature_share IS NULL
            AND NEW.burn_reason IS NOT NULL)
       ))
      OR
      (OLD.state = 'share_ready'
       AND (
           (NEW.state = 'completed' AND NEW.state_version = 4
            AND NEW.nonce_ciphertext IS NULL
            AND NEW.nonce_nonce IS NULL
            AND NEW.signing_package_digest = OLD.signing_package_digest
            AND NEW.signature_share = OLD.signature_share
            AND NEW.burn_reason IS NULL)
           OR
           (NEW.state = 'burned' AND NEW.state_version = 4
            AND NEW.nonce_ciphertext IS NULL
            AND NEW.nonce_nonce IS NULL
            AND NEW.signing_package_digest = OLD.signing_package_digest
            AND NEW.signature_share IS NULL
            AND NEW.burn_reason IS NOT NULL)
       ))
  )
BEGIN
    SELECT RAISE(ABORT, 'invalid FROST signer transition');
END;

CREATE TRIGGER IF NOT EXISTS frost_coordinator_sessions_no_delete
BEFORE DELETE ON frost_coordinator_sessions
BEGIN
    SELECT RAISE(ABORT, 'FROST coordinator tombstones cannot be deleted');
END;

CREATE TRIGGER IF NOT EXISTS frost_coordinator_sessions_transition
BEFORE UPDATE ON frost_coordinator_sessions
WHEN NEW.session_id <> OLD.session_id
  OR NEW.authorization_slot_id <> OLD.authorization_slot_id
  OR NEW.scope_id <> OLD.scope_id
  OR NEW.key_epoch <> OLD.key_epoch
  OR NEW.authorization_id <> OLD.authorization_id
  OR NEW.signing_message_digest <> OLD.signing_message_digest
  OR NEW.roster_digest <> OLD.roster_digest
  OR NEW.coordinator_id <> OLD.coordinator_id
  OR NEW.resource_fence <> OLD.resource_fence
  OR NEW.authorization_body_json <> OLD.authorization_body_json
  OR NEW.roster_json <> OLD.roster_json
  OR NEW.bound_checkpoint_digest <> OLD.bound_checkpoint_digest
  OR NEW.bound_checkpoint_json <> OLD.bound_checkpoint_json
  OR NEW.row_version <> OLD.row_version + 1
  OR NEW.source_store_uuid <> OLD.source_store_uuid
  OR NEW.source_owner_epoch < OLD.source_owner_epoch
  OR NEW.created_at_unix_ms <> OLD.created_at_unix_ms
  OR NEW.updated_at_unix_ms < OLD.updated_at_unix_ms
  OR NOT (
      (NEW.coordinator_worker_id = OLD.coordinator_worker_id
       AND NEW.coordinator_lease_id = OLD.coordinator_lease_id
       AND NEW.coordinator_owner_epoch = OLD.coordinator_owner_epoch
       AND NEW.lease_expires_at_unix_ms = OLD.lease_expires_at_unix_ms)
      OR
      (NEW.coordinator_worker_id = OLD.coordinator_worker_id
       AND NEW.coordinator_lease_id = OLD.coordinator_lease_id
       AND NEW.coordinator_owner_epoch = OLD.coordinator_owner_epoch
       AND NEW.lease_expires_at_unix_ms > OLD.lease_expires_at_unix_ms)
      OR
      ((NEW.coordinator_worker_id <> OLD.coordinator_worker_id
        OR NEW.coordinator_lease_id <> OLD.coordinator_lease_id)
       AND NEW.coordinator_owner_epoch = OLD.coordinator_owner_epoch + 1
       AND NEW.lease_expires_at_unix_ms > OLD.lease_expires_at_unix_ms)
  )
  OR NOT (
      (OLD.state = 'collecting_commitments'
       AND NEW.state = 'collecting_commitments'
       AND NEW.signing_participants_json IS NULL
       AND NEW.signing_package IS NULL AND NEW.signing_package_digest IS NULL
       AND NEW.shares_json = OLD.shares_json
       AND NEW.authorization_blob IS NULL AND NEW.authorization_blob_digest IS NULL
       AND NEW.availability_receipt IS NULL AND NEW.burn_reason IS NULL)
      OR
      (OLD.state = 'collecting_commitments'
       AND NEW.state = 'package_ready'
       AND NEW.commitments_json = OLD.commitments_json
       AND NEW.signing_participants_json IS NOT NULL
       AND NEW.signing_package IS NOT NULL AND NEW.signing_package_digest IS NOT NULL
       AND NEW.shares_json = OLD.shares_json
       AND NEW.authorization_blob IS NULL AND NEW.authorization_blob_digest IS NULL
       AND NEW.availability_receipt IS NULL AND NEW.burn_reason IS NULL)
      OR
      (OLD.state = 'package_ready'
       AND NEW.state = 'package_ready'
       AND NEW.commitments_json = OLD.commitments_json
       AND NEW.signing_participants_json = OLD.signing_participants_json
       AND NEW.signing_package = OLD.signing_package
       AND NEW.signing_package_digest = OLD.signing_package_digest
       AND NEW.authorization_blob IS NULL AND NEW.authorization_blob_digest IS NULL
       AND NEW.availability_receipt IS NULL AND NEW.burn_reason IS NULL)
      OR
      (OLD.state = 'package_ready'
       AND NEW.state = 'authorization_ready'
       AND NEW.commitments_json = OLD.commitments_json
       AND NEW.signing_participants_json = OLD.signing_participants_json
       AND NEW.signing_package = OLD.signing_package
       AND NEW.signing_package_digest = OLD.signing_package_digest
       AND NEW.shares_json = OLD.shares_json
       AND NEW.authorization_blob IS NOT NULL AND NEW.authorization_blob_digest IS NOT NULL
       AND NEW.availability_receipt IS NOT NULL AND NEW.burn_reason IS NULL)
      OR
      (OLD.state = 'authorization_ready'
       AND NEW.state = 'authorization_ready'
       AND NEW.commitments_json = OLD.commitments_json
       AND NEW.signing_participants_json = OLD.signing_participants_json
       AND NEW.signing_package = OLD.signing_package
       AND NEW.signing_package_digest = OLD.signing_package_digest
       AND NEW.shares_json = OLD.shares_json
       AND NEW.authorization_blob = OLD.authorization_blob
       AND NEW.authorization_blob_digest = OLD.authorization_blob_digest
       AND NEW.availability_receipt = OLD.availability_receipt
       AND NEW.burn_reason IS NULL)
      OR
      (OLD.state = 'authorization_ready'
       AND NEW.state = 'completed'
       AND NEW.commitments_json = OLD.commitments_json
       AND NEW.signing_participants_json = OLD.signing_participants_json
       AND NEW.signing_package = OLD.signing_package
       AND NEW.signing_package_digest = OLD.signing_package_digest
       AND NEW.shares_json = OLD.shares_json
       AND NEW.authorization_blob = OLD.authorization_blob
       AND NEW.authorization_blob_digest = OLD.authorization_blob_digest
       AND NEW.availability_receipt = OLD.availability_receipt
       AND NEW.burn_reason IS NULL)
      OR
      (OLD.state = 'completed'
       AND NEW.state = 'completed'
       AND NEW.commitments_json = OLD.commitments_json
       AND NEW.signing_participants_json = OLD.signing_participants_json
       AND NEW.signing_package = OLD.signing_package
       AND NEW.signing_package_digest = OLD.signing_package_digest
       AND NEW.shares_json = OLD.shares_json
       AND NEW.authorization_blob = OLD.authorization_blob
       AND NEW.authorization_blob_digest = OLD.authorization_blob_digest
       AND NEW.availability_receipt = OLD.availability_receipt
       AND NEW.burn_reason IS NULL)
      OR
      (OLD.state = 'package_ready'
       AND NEW.state = 'completed'
       AND NEW.commitments_json = OLD.commitments_json
       AND NEW.signing_participants_json = OLD.signing_participants_json
       AND NEW.signing_package = OLD.signing_package
       AND NEW.signing_package_digest = OLD.signing_package_digest
       AND NEW.shares_json = OLD.shares_json
       AND NEW.authorization_blob IS NOT NULL AND NEW.authorization_blob_digest IS NOT NULL
       AND NEW.availability_receipt IS NOT NULL AND NEW.burn_reason IS NULL)
      OR
      (OLD.state IN ('collecting_commitments', 'package_ready', 'authorization_ready')
       AND NEW.state = 'burned'
       AND NEW.commitments_json = OLD.commitments_json
       AND NEW.signing_participants_json IS OLD.signing_participants_json
       AND NEW.signing_package IS OLD.signing_package
       AND NEW.signing_package_digest IS OLD.signing_package_digest
       AND NEW.shares_json = OLD.shares_json
       AND NEW.authorization_blob IS OLD.authorization_blob
       AND NEW.authorization_blob_digest IS OLD.authorization_blob_digest
       AND NEW.availability_receipt IS OLD.availability_receipt
       AND NEW.burn_reason IS NOT NULL)
      OR
      (OLD.state = 'burned'
       AND NEW.state = 'burned'
       AND NEW.commitments_json = OLD.commitments_json
       AND NEW.signing_participants_json IS OLD.signing_participants_json
       AND NEW.signing_package IS OLD.signing_package
       AND NEW.signing_package_digest IS OLD.signing_package_digest
       AND NEW.shares_json = OLD.shares_json
       AND NEW.authorization_blob IS OLD.authorization_blob
       AND NEW.authorization_blob_digest IS OLD.authorization_blob_digest
       AND NEW.availability_receipt IS OLD.availability_receipt
       AND NEW.burn_reason = OLD.burn_reason)
  )
BEGIN
    SELECT RAISE(ABORT, 'invalid FROST coordinator transition');
END;

CREATE TRIGGER IF NOT EXISTS frost_projection_commits_immutable
BEFORE UPDATE ON frost_projection_commits
BEGIN
    SELECT RAISE(ABORT, 'FROST projection commits are immutable');
END;

CREATE TRIGGER IF NOT EXISTS frost_projection_commits_no_delete
BEFORE DELETE ON frost_projection_commits
BEGIN
    SELECT RAISE(ABORT, 'FROST projection commits are immutable');
END;
"#;