Đặc tả giao thức qub
qub là một giao thức cho các cam kết thời gian bằng mật mã: một hệ thống để niêm phong các từ ngữ đến một ngày trong tương lai và chứng minh, khi ngày đó đến, chính xác những gì đã được nói và khi nào.
Ba nguyên thủy làm cho nó hoạt động. drand là một beacon ngẫu nhiên phi tập trung — ngày công bố được thực thi bởi vật lý, không phải bởi thiện chí của bất kỳ bên nào. Nơi lưu trữ công khai vĩnh viễn là một kho công khai chống can thiệp — không bên nào có thể chỉnh sửa hoặc xóa một qub sau khi nó đã được niêm phong. ML-DSA-65 là một chữ ký số hậu lượng tử — mỗi qub được gắn với một cặp khóa mà bí mật của nó không bao giờ rời khỏi thiết bị của tác giả.
Cùng nhau, các nguyên thủy này tạo ra một tuyên bố bị khóa thời gian, chống giả mạo và có thể quy gán — một biên nhận có giá trị tăng lên khi khả năng giả mạo quá khứ của thế giới được cải thiện.
Phần còn lại của tài liệu này là đặc tả quy phạm cần thiết cho các triển khai có thể tương tác.
Đặc tả giao thức qub
| Trường | Giá trị |
|---|---|
| Phiên bản | 1.0 (phiên bản giao thức 0x01, phiên bản bao bọc bên ngoài 0x01) |
| Ngày | 2026-05-01 |
| Trạng thái | Bản thảo |
| Đã rà soát đến | 2026-05-01 |
Tài liệu này là đặc tả giao thức quy phạm cho hệ thống cam kết theo thời gian qub. Nó định nghĩa các cấu trúc dữ liệu, các quy tắc tuần tự hóa, các công thức suy dẫn và các quy trình xác minh cần thiết cho các triển khai có thể tương tác.
Phạm vi: lớp giao thức được cố ý làm trung lập về ngôn ngữ — phần thân của qub là các byte văn bản thuần / markdown / giao ước không rõ ràng, và việc hiển thị có nhận biết ngôn ngữ là trách nhiệm của người xem (ứng dụng web qub.social, iframe <qub-embed>, các trình khách MCP, v.v.).
1. Ký hiệu và quy ước
| Ký hiệu | Ý nghĩa |
|---|---|
u8, u64, i64 |
Số nguyên không dấu/có dấu với độ rộng bit được chỉ định |
[u8; N] |
Mảng byte có độ dài cố định N byte |
Vec<u8> |
Mảng byte có độ dài thay đổi |
Option<T> |
Giá trị kiểu T, hoặc vắng mặt |
String |
Chuỗi văn bản UTF-8, đã chuẩn hóa NFC |
| ` | |
SHA3-256(x) |
Hàm băm NIST SHA3-256 của chuỗi byte x (FIPS 202) |
ceil(x) |
Hàm trần: số nguyên nhỏ nhất ≥ x |
| CBOR | Biểu diễn đối tượng nhị phân súc tích (RFC 8949) |
| big-endian | Byte có ý nghĩa nhất đứng trước |
Tất cả các số nguyên trong các cấu trúc tiền ảnh được mã hóa thành mảng byte có độ rộng cố định big-endian (i64 → 8 byte, u8 → 1 byte) trừ khi có quy định khác.
Tất cả các dấu thời gian là giây Unix theo UTC.
2. Cấu trúc dữ liệu
2.1 ComposeQub (trạng thái trong bộ nhớ của người tạo)
Không tuần tự hóa thành CBOR. Không ghi vào nơi lưu trữ vĩnh viễn. Cục bộ trong ứng dụng của người tạo.
ComposeQub {
draft_id: [u8; 16], // Random, generated locally
created_at: i64, // Unix seconds UTC
unlock_at: Option<i64>, // Unix seconds UTC; None while composing
visibility: u8, // 0x01 = public (only value in MVP)
content_type: u8, // 0x01 = text (only value in MVP)
plaintext: Vec<u8>, // UTF-8 qub body
sender_label: Option<String>, // Decorative display name; not authenticated
status: DraftStatus, // Composing | Sealed | Uploaded | Failed
}
2.2 QubEnvelope (tải trọng đã giải mã)
Tuần tự hóa bằng CBOR chuẩn (§3). Được mã hóa bên trong SealedQub. Đây là cấu trúc chứng minh tính toàn vẹn của nội dung sau khi giải mã.
QubEnvelope {
version: u8, // Protocol major version (0x01 for v1)
qub_id: [u8; 32], // Derived (see §4.1)
content_type: u8, // Content type registry (see §6)
created_at: i64, // Unix seconds UTC
unlock_at: i64, // Unix seconds UTC
outcome_at: Option<i64>, // V1.1 — when reality renders judgment (verdict-uplift-plan §3.1)
sender_label: Option<String>, // Decorative; not authenticated in MVP
reply_to: Option<[u8; 32]>,// Parent qub_id for reply chains; not in qub_id preimage; not signed (see §9.3)
body: Vec<u8>, // Content payload (UTF-8 for text, CBOR for pact)
body_hash: [u8; 32], // SHA3-256(body) (see §4.2)
sig_alg: u8, // Signature algorithm (see §9.2)
author_signature: Option<Vec<u8>>, // Set when sig_alg != 0x00
author_pubkey: Option<Vec<u8>>, // Set when sig_alg != 0x00
cosigner_pubkey: Option<Vec<u8>>, // Set for cosigned pact bilateral agreements
cosigner_signature: Option<Vec<u8>>, // Set for cosigned pact bilateral agreements
}
Cơ sở (qub văn bản không ký): version = 0x01, content_type = 0x01, sig_alg = 0x00, tất cả các trường Option vắng mặt.
Các cấu hình v1 khác: content_type = 0x03 (thân giao ước, xem §6.1); sig_alg = 0x01 (ML-DSA-65) với author_signature và author_pubkey hiện diện (xem §9.3); cosigner_pubkey và cosigner_signature hiện diện cùng nhau cho các giao ước đồng ký (xem §9.7); reply_to được đặt thành qub_id của qub cha cho các qub chuỗi trả lời (xem §9.3 để biết các hệ quả về phạm vi chữ ký).
2.3 SealedQub (định dạng wire chuẩn)
Tuần tự hóa bằng CBOR chuẩn (§3). Được ghi vào nơi lưu trữ vĩnh viễn. Đây là tạo phẩm trên chuỗi.
SealedQub {
version: u8, // Protocol major version (0x01 for v1)
qub_id: [u8; 32], // Same as QubEnvelope.qub_id
visibility: u8, // 0x01 = public; v1 viewers reject other values
unlock_at: i64, // Unix seconds UTC
outcome_at: Option<i64>, // V1.1 — surfaced on the verdict-watch CTA
// before reveal; mirrors QubEnvelope.outcome_at;
// bound to qub_id via the §4.1 preimage.
drand_chain_id: String, // drand chain hash (hex string)
drand_round: u64, // Target drand round number
tlock_ciphertext: Vec<u8>, // tlock-encrypted QubEnvelope CBOR bytes
recipient_pubkey: Option<[u8; 32]>,// Reserved field; accepted by canonical CBOR
// but not interpreted by the v1 reference viewer
title: Option<String>, // Plaintext title surfaced on the viewer
// countdown before reveal. Bound to qub_id
// via title_hash (§4.1). 1..=100 NFC code
// points, no control characters.
}
2.4 RevealedQub (trạng thái ứng dụng của người xem)
Không tuần tự hóa thành CBOR. Cục bộ trong ứng dụng của người xem. Được xây dựng sau khi giải mã và xác minh thành công.
RevealedQub {
qub_id: [u8; 32],
arweave_tx_id: String,
visibility: u8,
content_type: u8,
created_at: i64,
unlock_at: i64,
outcome_at: Option<i64>, // V1.1 — chuyển tiếp từ QubEnvelope.outcome_at / SealedQub.outcome_at; điều khiển khối theo dõi phán quyết trên trang công bố (verdict-uplift-plan §5.1)
drand_chain_id: String,
drand_round: u64,
sender_label: Option<String>,
title: Option<String>, // Carried forward from SealedQub.title
reply_to: Option<[u8; 32]>,
body: Vec<u8>,
body_hash: [u8; 32],
body_hash_verified: bool,
author_signature: Option<Vec<u8>>,
author_pubkey: Option<Vec<u8>>,
signature_verified: Option<bool>,
cosigner_pubkey: Option<Vec<u8>>,
cosigner_signature: Option<Vec<u8>>,
cosigner_verified: Option<bool>,
}
3. Hồ sơ CBOR chuẩn
Tất cả việc tuần tự hóa SealedQub và QubEnvelope PHẢI tuân thủ hồ sơ này. Hai triển khai khi nhận cùng một cấu trúc logic PHẢI tạo ra các byte giống hệt nhau.
3.1 Quy tắc mã hóa
| Quy tắc | Đặc tả |
|---|---|
| Chuẩn | RFC 8949 §4.2.1 (Yêu cầu mã hóa xác định cốt lõi) |
| Thứ tự khóa của map | Sắp xếp theo độ dài byte đã mã hóa trước (ngắn hơn trước dài hơn), sau đó theo từ điển (từng byte đối với các mã hóa cùng độ dài) |
| Mã hóa số nguyên | Dạng ngắn nhất: 0–23 trong byte đầu; 24–255 trong 2 byte; 256–65535 trong 3 byte; v.v. |
| Mã hóa độ dài | Chỉ độ dài xác định. Không có mảng, map, chuỗi byte hoặc chuỗi văn bản độ dài không xác định (thông tin bổ sung = 31 bị cấm). |
| Tags | Không có tags CBOR (kiểu chính 6 bị cấm). |
| Dấu phẩy động | Không có float (kiểu chính 7 giá trị 0xF9–0xFB bị cấm). |
| Chuỗi văn bản | Mã hóa UTF-8, đã chuẩn hóa NFC (Unicode Normalization Form C). |
| Chuỗi byte | Byte thô. Không có mã hóa base64 ở lớp CBOR. |
| Khóa trùng lặp | Từ chối kèm lỗi. Các bộ phân tích cú pháp KHÔNG ĐƯỢC chấp nhận thầm lặng các khóa map trùng lặp. |
| Khóa không xác định | Từ chối kèm lỗi. Các bộ phân tích cú pháp KHÔNG ĐƯỢC chấp nhận các khóa map nằm ngoài tập khóa chuẩn của kiểu — hai chuỗi byte chuẩn khác nhau không bao giờ được giải mã về cùng một giá trị (encode(decode(x)) == x), và đối với các tải trọng đã ký, một khóa bổ sung sẽ là nội dung ẩn mà cả hai chữ ký đều cam kết. Việc tiến hóa schema đi qua version, không bao giờ qua các khóa bổ sung. |
| Giá trị đơn | Chỉ true (0xF5), false (0xF4) và null (0xF6) được cho phép. |
| Trường tùy chọn | Các trường tùy chọn vắng mặt bị bỏ qua hoàn toàn khỏi map CBOR (không được mã hóa thành null). Các trường tùy chọn hiện diện được bao gồm theo thứ tự khóa đã sắp xếp. |
3.2 Thứ tự khóa chuẩn đã xác minh
Các thứ tự khóa này là quy phạm. Các triển khai PHẢI phát các khóa chính xác theo thứ tự này. Các khẳng định debug NÊN xác minh thứ tự trong các bản dựng không phải release.
QubEnvelope (phiên bản 0x01, không ký, tất cả các trường tùy chọn vắng mặt):
"body" (5 encoded bytes)
"qub_id" (7 encoded bytes)
"sig_alg" (8 encoded bytes)
"version" (8 encoded bytes)
"reply_to" (9 encoded bytes) ← only if present (reply chains)
"body_hash" (10 encoded bytes)
"unlock_at" (10 encoded bytes)
"created_at" (11 encoded bytes)
"outcome_at" (11 encoded bytes) ← only if present (V1.1 verdict mechanic)
"content_type" (13 encoded bytes)
"sender_label" (13 encoded bytes) ← only if present
"author_pubkey" (14 encoded bytes) ← only if present
"cosigner_pubkey" (16 encoded bytes) ← only if present (pact cosign)
"author_signature" (17 encoded bytes) ← only if present
"cosigner_signature" (19 encoded bytes) ← only if present (pact cosign)
Suy dẫn thứ tự khóa QubEnvelope: mỗi khóa là một chuỗi văn bản CBOR. Độ dài đã mã hóa = 1 byte header + độ dài chuỗi (cho các chuỗi dưới 24 byte). Sắp xếp theo tổng độ dài đã mã hóa trước, sau đó theo từ điển cho các khóa cùng độ dài.
SealedQub (phiên bản 0x01, công khai, không người nhận):
"title" (6 encoded bytes) ← only if present
"qub_id" (7 encoded bytes)
"version" (8 encoded bytes)
"unlock_at" (10 encoded bytes)
"outcome_at" (11 encoded bytes) ← only if present (V1.1 verdict mechanic)
"visibility" (11 encoded bytes)
"drand_round" (12 encoded bytes)
"drand_chain_id" (15 encoded bytes)
"recipient_pubkey" (17 encoded bytes) ← only if present
"tlock_ciphertext" (17 encoded bytes)
PactTerms (thân giao ước, content_type 0x03):
"notes" (6 encoded bytes) ← only if present
"terms" (6 encoded bytes)
"title" (6 encoded bytes)
"party_a" (8 encoded bytes)
"party_b" (8 encoded bytes)
"pact_version" (13 encoded bytes)
PactTerm (hàng của mảng terms):
"key" (4 encoded bytes)
"value" (6 encoded bytes)
PartyIdentifier (map party_a / party_b):
"label" (6 encoded bytes)
"contact" (8 encoded bytes) ← only if present
3.3 Tham chiếu mã hóa byte
| Kiểu | Mã hóa CBOR | Ví dụ |
|---|---|---|
| Hàm băm SHA3-256 (32 byte) | 0x58 0x20 + 32 byte |
body_hash, qub_id |
| Dấu thời gian (i64) | Kiểu chính 0 (dương) hoặc 1 (âm), mã hóa ngắn nhất | giây Unix |
| Phiên bản (u8, giá trị 1) | 0x01 (một byte) |
|
| Loại nội dung (u8, giá trị 1) | 0x01 (một byte) |
|
| sig_alg (u8, giá trị 0) | 0x00 (một byte) |
|
| Chữ ký ML-DSA-65 (3.309 byte) | 0x59 0x0C 0xED + 3.309 byte |
author_signature, cosigner_signature |
| Khóa công khai ML-DSA-65 (1.952 byte) | 0x59 0x07 0xA0 + 1.952 byte |
author_pubkey, cosigner_pubkey |
4. Các suy dẫn quy phạm
4.1 qub_id
qub_id xác định duy nhất một qub và liên kết QubEnvelope với SealedQub. Nó được suy dẫn một cách xác định từ nội dung của envelope.
qub_id = SHA3-256(
"QUB_ID_V2" || // domain separator: ASCII bytes [0x51 0x55 0x42 0x5F 0x49 0x44 0x5F 0x56 0x32] (9 bytes) + 0x00 padding (1 byte) = 10 bytes
version || // u8 (1 byte)
content_type || // u8 (1 byte)
created_at || // i64 big-endian (8 bytes)
unlock_at || // i64 big-endian (8 bytes)
outcome_at_or_zero || // i64 big-endian (8 bytes; 0 when outcome_at is absent)
drand_round || // u64 big-endian (8 bytes)
body_hash || // [u8; 32] (32 bytes)
title_hash // [u8; 32] (32 bytes; absent-sentinel = [0u8; 32])
)
// Total preimage: 108 bytes → 32-byte output
Mã hóa dấu phân cách miền: Chuỗi "QUB_ID_V2" là 9 byte ASCII. Một byte đệm 0x00 đơn được nối thêm để đạt 10 byte cho việc căn chỉnh. Các triển khai PHẢI sử dụng chính xác 10 byte này: [0x51, 0x55, 0x42, 0x5F, 0x49, 0x44, 0x5F, 0x56, 0x32, 0x00].
Mã hóa outcome_at: V1.1 đã mở rộng tiền ảnh từ 92 lên 100 byte để gấp trường outcome_at tùy chọn vào liên kết. outcome_at vắng mặt được mã hóa thành 8 byte zero; các bộ xác thực giao thức từ chối outcome_at <= 0 ở mọi nơi nên trị canh gác này không thể trùng với một giá trị hợp lệ. Xem §3.2 (định dạng wire) và tài liệu trong cây nguồn tasks/verdict-uplift-plan.md để biết cơ chế verdict thúc đẩy trường này.
Mã hóa drand_round: V1.2 đã mở rộng tiền ảnh từ 100 lên 108 byte để gấp drand_round (vòng drand mục tiêu, §4.3) vào liên kết, và nâng dấu phân cách miền lên QUB_ID_V2. Điều này liên kết vòng khóa thời gian vào danh tính qub: một cổng không thể tái liên kết bản mã với một vòng khác (ví dụ một vòng đã qua) so với vòng mà unlock_at được hiển thị ngụ ý. Thủ tục mở khóa (§8) còn xác minh thêm rằng vòng được nung vào stanza bản mã tlock khớp với unlock_round(unlock_at), nên thời điểm mở khóa được hiển thị có thể chứng minh được là vòng thực sự kiểm soát việc giải mã.
Tính chất:
- Thay đổi bất kỳ trường nào trong QubEnvelope (body, dấu thời gian, loại nội dung, phiên bản) tạo ra một qub_id khác.
- qub_id được tính toán trước khi mã hóa. Cả QubEnvelope và SealedQub đều mang cùng một qub_id. Người xem xác minh chúng khớp sau khi giải mã.
- qub_id không phụ thuộc vào
sender_label,author_signature, hoặcauthor_pubkey. Điều này có nghĩa là cùng một nội dung được niêm phong cùng một lúc tạo ra cùng một qub_id bất kể ai ký nó. - Thay đổi
titlecủa SealedQub (với mọi thứ khác cố định) thay đổiqub_idthông quatitle_hash. Do đó, một cổng không thể đổi tiêu đề văn bản thuần được hiển thị trên đồng hồ đếm ngược mà không làm mất hiệu lực danh tính qub. - Thay đổi
outcome_atcủa SealedQub (với mọi thứ khác cố định) thay đổiqub_idthông qua tiền ảnh. Một cổng không thể đổi ngày verdict-on trước-công-bố được hiển thị trên đồng hồ đếm ngược mà không làm mất hiệu lực danh tính qub. - Thay đổi
drand_round(với mọi thứ khác cố định) thay đổiqub_idthông qua tiền ảnh. Một cổng không thể tái liên kết bản mã khóa thời gian với một vòng khác mà không làm mất hiệu lực danh tính qub; kết hợp với kiểm tra vòng-stanza tại thời điểm mở khóa ở §8,unlock_atđược hiển thị là vòng thực sự kiểm soát việc giải mã.
4.2 body_hash
body_hash = SHA3-256(body)
Trong đó body là tải trọng nội dung Vec<u8> thô. Đối với các qub văn bản, đây là thân qub được mã hóa UTF-8.
4.2.1 title_hash
title_hash = SHA3-256(NFC(title).utf8_bytes) if title is present
title_hash = [0u8; 32] if title is absent
Trong đó title là tiêu đề văn bản thuần tùy chọn được hiển thị trên đồng hồ đếm ngược của người xem trước khi công bố (xem §3.2). Chuẩn hóa NFC chạy tại thời điểm băm để bản tóm tắt ổn định trên các chuỗi điểm mã tương đương về mặt thị giác. Trị canh gác toàn số 0 được dành riêng cho trường hợp vắng mặt; một chuỗi rỗng bị từ chối ở ranh giới CBOR chuẩn như một mã hóa không chuẩn của "vắng mặt" (mã hóa chuẩn loại bỏ hoàn toàn trường này).
4.3 Ánh xạ vòng mở khóa
drand_round = ceil((unlock_at - chain_genesis_time) / chain_period_seconds)
| Tham số | Nguồn | Ví dụ |
|---|---|---|
unlock_at |
Giây Unix UTC do người dùng chọn | 1735689600 (2025-01-01 00:00:00 UTC) |
chain_genesis_time |
thông tin chuỗi drand (genesis_time) |
1595431050 |
chain_period_seconds |
thông tin chuỗi drand (period) |
30 |
Thao tác ceil() chọn vòng drand đầu tiên có thời gian công bố ≥ unlock_at. Điều này đảm bảo qub không trở nên có thể giải mã được trước thời điểm mở khóa đã chọn.
Trường hợp biên: nếu (unlock_at - chain_genesis_time) chia hết chính xác cho chain_period_seconds, kết quả là chính vòng đó — qub mở khóa chính xác tại thời điểm công bố của vòng đó.
Xác thực: unlock_at PHẢI ở trong tương lai tại thời điểm niêm phong. unlock_at KHÔNG ĐƯỢC vượt quá 10 năm kể từ created_at (để hạn chế rủi ro phụ thuộc drand dài hạn; UI NÊN cảnh báo cho các ngày mở khóa vượt quá 2 năm).
5. Newtype định dạng wire
Các newtype định dạng wire cung cấp sự an toàn tại thời điểm biên dịch chống lại việc nhầm lẫn các byte CBOR với JSON, văn bản thuần thô, hoặc các mã hóa byte khác.
| Kiểu | Chứa | Được tạo bởi | Được tiêu thụ bởi |
|---|---|---|---|
SealedQubCbor |
CBOR chuẩn của SealedQub | serialize_sealed_qub() |
tải lên nơi lưu trữ vĩnh viễn, người xem fetch |
QubEnvelopeCbor |
CBOR chuẩn của QubEnvelope | serialize_qub_envelope() |
đầu vào mã hóa tlock, đầu ra giải mã tlock |
5.1 Quy tắc xây dựng
// Production code — only through CBOR serialisers:
let sealed = SealedQubCbor::from_encoded(cbor_bytes);
// There is deliberately NO From<Vec<u8>> implementation.
// You cannot accidentally wrap arbitrary bytes in a wire format type.
// Accessing raw bytes:
let bytes: &[u8] = sealed.as_bytes();
let bytes: Vec<u8> = sealed.into_bytes();
5.2 Xác thực khi xây dựng
from_encoded() NÊN xác thực rằng đầu vào bắt đầu bằng một header map CBOR hợp lệ. Việc xác thực cấu trúc đầy đủ xảy ra tại thời điểm phân tích, không phải thời điểm xây dựng, để tránh phân tích kép.
6. Sổ đăng ký loại nội dung
| Giá trị | Loại | Kích thước thân tối đa | Ghi chú |
|---|---|---|---|
0x00 |
Dành riêng (không hợp lệ) | — | KHÔNG ĐƯỢC sử dụng |
0x01 |
Văn bản thuần (UTF-8, Markdown hạn chế) | 50 KB trả phí / 10 KB miễn phí | Xem §10 để biết quy tắc hiển thị. Phân chia miễn phí / trả phí được thực thi bởi dịch vụ tải lên; trần cứng ở lớp giao thức là 50 KB. |
0x02 |
Dành riêng (tương lai) | — | Được cấp phát cho một loại nội dung trong tương lai; không hợp lệ trong v1. Người xem PHẢI từ chối theo quy tắc bên dưới. |
0x03 |
Giao ước (thỏa thuận song phương, thân CBOR) | 100 KB | Thân là CBOR chuẩn PactTerms (§6.1). Ký bởi người đồng ký theo §9.7. |
0x04 |
Phán quyết (người tạo tự chấm điểm, thân CBOR) | 8 KB | Thân là CBOR chuẩn VerdictBody (§6.2). Chỉ được phát ra bởi intent phía hệ thống verdict. Quan hệ với qub cha nằm ở tag Arweave Parent-Tx-Id, không nằm trong thân. Xem verdict-uplift-plan §3.4. |
Người xem PHẢI từ chối các loại nội dung không xác định kèm theo lỗi rõ ràng cho người dùng. Người xem KHÔNG ĐƯỢC cố gắng hiển thị các loại không xác định dưới dạng văn bản.
6.1 Thân giao ước (content_type = 0x03)
Một thân giao ước là mã hóa CBOR chuẩn của giá trị PactTerms:
PactTerms {
pact_version: u8, // 0x01 for structured/v1
title: String, // ≤ 200 bytes, NFC
terms: Vec<PactTerm>, // ≤ 20 rows
party_a: PartyIdentifier, // initiator
party_b: PartyIdentifier, // counter-signer
notes: Option<String>, // ≤ 5,000 bytes, NFC; absent key if none
}
PactTerm { key: String (≤ 100), value: String (≤ 2,000) } // NFC on both sides
PartyIdentifier{ label: String (≤ 100), contact: Option<String (≤ 320)> }
Các thứ tự khóa CBOR chuẩn cho cả ba map được nêu trong §3.2. Tổng CBOR giao ước đã tuần tự hóa KHÔNG ĐƯỢC vượt quá 100 KB (khớp với §6).
Bộ phân biệt schema. Hàng đầu tiên trong terms cho một giao ước structured/v1 PHẢI là { key: "pact_schema", value: "structured/v1" }. Các hàng không có dấu hiệu này là các giao ước "tùy chỉnh" và không nhận được xác thực cấu trúc hoặc hiển thị nhận biết schema.
Các vị trí xác nhận đã đóng băng. Các giao ước structured/v1 mang chính xác bốn hàng xác nhận dưới các khóa này:
"initiator_standard_terms"
"initiator_capacity_terms"
"counterparty_standard_terms"
"counterparty_capacity_terms"
value cho mỗi cái là một trong tám chuỗi tiếng Anh đã đóng băng được chọn bởi cặp (role, kind), trong đó role ∈ { seller, buyer, provider, client } và kind ∈ { standard, capacity }. Bản thân các chuỗi là dữ liệu giao thức quy phạm — các chữ ký ML-DSA-65 của cả hai bên cam kết với các byte chính xác thông qua body_hash. Chúng KHÔNG được bản địa hóa; thân được ký là trung lập về ngôn ngữ. Bất kỳ thay đổi từ ngữ nào đều yêu cầu một phiên bản schema mới (structured/v2).
Tám chuỗi, cách tra cứu của chúng (acknowledgement_for(role, kind)), và lý do căn bản cho mỗi cái được ghim bởi triển khai tham chiếu. Các triển khai tuân thủ PHẢI phát ra các giá trị xác nhận giống hệt byte; các kiểm tra body-hash SHA3-256 cố định bao phủ cả bốn tổ hợp vai trò bắt được mọi sự trôi dạt.
Thứ tự hiển thị của người xem. Các chuỗi xác nhận chứa các cụm từ như "described above", giả định rằng các hàng mô tả / phạm vi hiển thị trước các xác nhận. Người xem PHẢI hiển thị mảng terms theo thứ tự CBOR; sắp xếp lại sẽ phá vỡ ngữ nghĩa văn bản.
Liên hệ của bên đối tác. Khi contact của Bên B là một địa chỉ email hợp lệ, dịch vụ tải lên qub tự động gửi một email mời xem xét / đồng ký tại thời điểm staging và ràng buộc đồng ký cuối cùng với việc xác minh cùng một địa chỉ đó (§9.7). Các giao ước mà liên hệ Bên B vắng mặt vẫn có thể được đồng ký, nhưng chỉ qua một kênh ngoài băng — dịch vụ từ chối các yêu cầu đồng ký không thể tạo ra một dấu hiệu xác minh email 15 phút khớp.
6.2 Thân phán quyết (content_type = 0x04)
Một thân phán quyết là mã hóa CBOR chuẩn của giá trị VerdictBody:
VerdictBody {
verdict_version: u8, // 0x01 for structured/v1
outcome: u8, // 1=Right · 2=Partial · 3=Wrong · 4=Unfalsifiable
reflection: Option<String>, // ≤ 2,000 bytes NFC; "what changed, what did you learn"
evidence_url: Option<String>, // ≤ 2,048 bytes; HTTPS only; absent key when omitted
}
Thứ tự khóa CBOR chuẩn:
"outcome" (8 encoded bytes)
"reflection" (11 encoded bytes) ← only if present
"evidence_url" (13 encoded bytes) ← only if present
"verdict_version" (16 encoded bytes)
Tổng CBOR phán quyết đã tuần tự hóa KHÔNG ĐƯỢC vượt quá 8 KB (khớp với hàng đăng ký bên trên).
Enum kết quả. Byte trên wire là trung lập về intent; bốn nhóm Right / Partial / Wrong / Unfalsifiable bao phủ không gian kết quả của mọi intent có phán quyết. Nhãn theo từng intent ("Đoán trúng" / "Đã giữ lời" / "Hoàn thành" / "Đã được xác nhận" cho Right, v.v.) là vấn đề hiển thị phía người xem, được phân giải dựa vào intent của qub cha — wire vẫn trung lập về ngôn ngữ và intent. Các giá trị ngoài khoảng 1..=4 PHẢI bị từ chối khi giải mã.
Liên kết với qub cha. Một qub phán quyết KHÔNG mang tham chiếu tới qub cha trong thân của nó. Mã giao dịch Arweave của qub cha được phát ra dưới dạng tag lưu trữ Parent-Tx-Id tại thời điểm tải lên (§7 lớp tag lưu trữ). Điều này giữ cho thân là một tuyên bố tự đánh giá đã ký, độc lập; chuỗi kiểm toán ("đúng về điều gì?") được thiết lập qua việc tra cứu tag Arweave.
An toàn URL bằng chứng (quy phạm). Khi evidence_url hiện diện, các trình xác thực (phía compose, phía wire, edge Worker) PHẢI thực thi:
- Chỉ HTTPS. Chuỗi PHẢI bắt đầu bằng dãy byte
https://. Bất kỳ scheme nào khác —http,ftp,javascript,data,file, v.v. — đều bị từ chối. - Giới hạn độ dài. ≤ 2.048 byte (giới hạn thực tế của URL trình duyệt).
- Kiểm tra NFC và điểm mã thù địch. Cùng quy tắc như
titlevàreflection— các điểm mã bidi-override / zero-width / tag-block / BOM / C0 / C1 bị từ chối. Định nghĩa khớp với Rustcrate::handle::contains_hostile_text_codepointvà TSworkers/api/src/utils/unicode.ts::isHostileCodepoint(giữ đồng bộ). - Không khoảng trắng, không ký tự điều khiển ASCII. Khoảng trắng / DEL / byte dưới
0x20ở bất kỳ vị trí nào trong URL đều bị từ chối — đóng vector tấn công\n/\tmà quy tắc bidi không bao phủ. - Phân đoạn host không rỗng. Mọi thứ giữa
https://và ký tự/,?hoặc#đầu tiên PHẢI không rỗng.
Không lấy nội dung từ phía máy chủ. Worker KHÔNG ĐƯỢC proxy, fetch, hoặc xem trước URL. Giao thức lưu một chuỗi; việc hiển thị diễn ra phía người xem với rel="nofollow noopener noreferrer" target="_blank" và một host nhìn thấy được kèm theo văn bản liên kết.
Phản tư. Văn bản phản tư tùy chọn do người tạo viết ("điều gì đã thay đổi, bạn đã học được gì"). Cùng kiểm tra NFC và điểm mã thù địch như title. Đầu vào rỗng / chỉ có khoảng trắng sẽ gập lại thành vắng mặt tại thời điểm xây dựng.
Phiên bản schema. v1 chỉ hỗ trợ verdict_version = 0x01. Các bản sửa schema trong tương lai sẽ tăng byte này và xuất hiện cùng với một phiên bản giao thức mới theo §12.
7. Giao thức niêm phong
Trình tự niêm phong hoàn chỉnh. Mỗi bước là quy phạm.
1. User composes plaintext and metadata in ComposeQub.
2. Validate:
a. body is non-empty.
b. body size ≤ max for content_type and user tier (see §6).
c. unlock_at is in the future.
d. unlock_at ≤ created_at + 10 years.
e. content_type is a known, supported value.
3. Compute body_hash = SHA3-256(body).
4. Set created_at = current Unix seconds UTC.
5. Select drand chain. Load chain_genesis_time and chain_period_seconds, and
compute drand_round = ceil((unlock_at - chain_genesis_time) / chain_period_seconds).
(Computed here, before qub_id, because drand_round is bound into the qub_id
preimage — §4.1, V1.2.)
6. Compute qub_id (see §4.1), folding in drand_round from step 5.
7. Construct QubEnvelope with all fields.
8. Serialise QubEnvelope using canonical CBOR → bytes B.
Assert: serialised output matches canonical profile (§3).
9. Compute C = tlock_encrypt(B, drand_round, drand_chain_public_key).
10. Construct SealedQub with tlock_ciphertext = C, and matching qub_id, version,
unlock_at, drand_chain_id, drand_round.
12. Serialise SealedQub using canonical CBOR → SealedQubCbor.
12a. Generate K = 32 random bytes (CSPRNG) and N = 12 random bytes (CSPRNG).
Compute W = wrap_sealed_qub(SealedQubCbor, qub_id=qub_id, key=K, nonce=N)
per §13. The bytes uploaded to permanent storage are the OuterWrapper CBOR W,
never the bare SealedQubCbor. K leaves the device only as the URL
fragment in step 16.
13. Display seal-time disclosure. User confirms.
14. Validate upload eligibility via the qub upload service (bot-detection, entitlement, rate limits).
15. Submit W (the OuterWrapper bytes) to the qub upload service; the service
signs and uploads to permanent storage. The service is byte-blind to the inner
SealedQubCbor and never receives K.
16. Receive arweave_tx_id from the service. Construct delivery URL as
`<origin>/c/<arweave_tx_id>#<base64url(K)>` (or `<origin>/s/<short_code>#<base64url(K)>`
when a short code is allocated). Browsers do not transmit URL fragments
to servers, so K is never observed by qub.social or any storage gateway.
Lớp tag lưu trữ (ngoài băng). Dịch vụ tải lên qub gắn một tập hợp cố ý nhỏ các tag giao dịch lưu trữ cùng với tải trọng đã bao bọc. Content-Type=application/octet-stream được quy định bắt buộc. Dịch vụ tham chiếu bổ sung gắn ba tag tùy chọn khi người tạo chọn hiển thị chúng: Intent (ý định soạn được xác thực theo danh sách cho phép — ví dụ: quote, reply, commitment), Author (vân tay khóa công khai §9.3 của người tạo dưới dạng hex chữ thường 64 ký tự), và Parent-Tx-Id (ID giao dịch lưu trữ của qub cha cho các chuỗi trả lời, 43 ký tự base64url).
Tag Author là chọn tham gia trên mỗi qub: ứng dụng người tạo tham chiếu chỉ gắn nó khi người dùng kích hoạt rõ ràng quy gán công khai tại thời điểm niêm phong. Khi công tắc tắt — mặc định — không có tag Author nào được ghi và qub không được quy gán trên chuỗi: không có gì trong nơi lưu trữ vĩnh viễn liên kết bản tải lên với handle, email, hoặc các qub khác của người tạo. Khi công tắc bật, vân tay Author phân giải đến @handle đã chọn của người tạo thông qua chuỗi chứng thực §9.5. Các mối quan hệ chuỗi trả lời và Intent không có tính nhận dạng. Lớp bao bọc bên ngoài (§13) bảo vệ thân bên trong khỏi sự tương quan của ciphertext — ngăn người thu thập nhận diện và giải mã hàng loạt các bản tải lên hình dạng qub sau khi vòng drand của chúng được công bố.
Dịch vụ tham chiếu cố ý KHÔNG gắn các tag App-Name, App-Version, hoặc Type: bất kỳ bộ lọc giá trị đơn nào như vậy sẽ trả về toàn bộ kho qub cho một truy vấn GraphQL, điều này không nhất quán với phạm vi bảo mật chỉ-thân của lớp bao bọc.
Một trình xác minh tuân thủ KHÔNG ĐƯỢC phụ thuộc vào bất kỳ tag lưu trữ nào cho việc xác minh bên thứ ba §11; body hash / qub_id / chữ ký chỉ cam kết với CBOR bên trong, không bao giờ với tập hợp tag.
8. Giao thức mở khóa
Trình tự mở khóa hoàn chỉnh. Mỗi bước là quy phạm.
1. Viewer opens delivery URL. Extract arweave_tx_id from path AND
K = base64url_decode(fragment) from the URL fragment. If the fragment
is absent or malformed → display "this URL is missing its decryption
key" and stop; the viewer MUST NOT contact the storage gateway
without K, since fetching wrapped bytes the viewer cannot decrypt
serves no purpose and only leaks the access attempt.
2. Check denylist. If tx_id is denylisted → display block message. Stop.
3. Fetch OuterWrapper bytes from permanent storage (with multi-gateway fallback).
3a. Unwrap: parse the bytes as OuterWrapper (§13), verify the wrapper
`version` byte is `0x01`, and compute SealedQubCbor =
unwrap_sealed_qub(OuterWrapper, key=K). Any AEAD authentication
failure (wrong K, tampered ciphertext, swapped qub_id-as-AAD,
swapped nonce) → display "this URL's decryption key does not match
the stored qub" and stop. Authentication failures are
indistinguishable to the viewer per §13.5.
4. Parse SealedQubCbor → SealedQub.
5. Validate: SealedQub.version is known (0x01). Reject unknown versions.
6. If current time < SealedQub.unlock_at → display countdown. Poll or wait.
6a. Round-binding check (V1.2). Recompute expected_round =
ceil((SealedQub.unlock_at - chain_genesis_time) / chain_period_seconds).
Reject unless SealedQub.drand_round == expected_round AND the round baked
into the tlock ciphertext stanza (read via the age/tlock header, no signature
required) == expected_round. The stanza round is the one that actually gates
decryption; without this check a malicious creator could bind the ciphertext
to an already-past round while displaying a future countdown, so anyone
reading the stored bytes could decrypt before unlock_at. Implementations with
no chain identity (test mocks) skip this check.
7. Once current time ≥ SealedQub.unlock_at:
a. Fetch drand round signature for SealedQub.drand_round from drand network.
b. Compute B = tlock_decrypt(SealedQub.tlock_ciphertext, round_signature).
8. Parse B → QubEnvelope.
9. Validate QubEnvelope.version is known.
10. Verify: SHA3-256(QubEnvelope.body) == QubEnvelope.body_hash.
Fail → integrity error.
11. Verify: QubEnvelope.qub_id == SealedQub.qub_id.
Fail → integrity error.
12. Verify: QubEnvelope.unlock_at == SealedQub.unlock_at.
Fail → integrity error.
13. Verify: QubEnvelope.content_type is known and renderable.
Known values: 0x01 (text), 0x03 (pact). Unknown → display error.
14. If QubEnvelope.sig_alg != 0x00 → verify author signature (see §9.4).
15. If cosigner_pubkey or cosigner_signature present → verify cosigner (see §9.7).
16. Render content using appropriate renderer (see §10 for text, §6 for pact).
17. Construct RevealedQub for display.
9. Ký quyền tác giả
9.1 Lý do căn bản
Các qub được lưu trữ trong nơi lưu trữ vĩnh viễn. Các chữ ký quyền tác giả phải vẫn không thể giả mạo vô thời hạn, đó là lý do tại sao v1.0 sử dụng sơ đồ hậu lượng tử ML-DSA-65 (FIPS 204) thay vì một sơ đồ cổ điển mà tính bảo mật có thể suy giảm trong vòng đời vĩnh viễn của qub.
9.2 Sổ đăng ký thuật toán
sig_alg |
Sơ đồ | Kích thước khóa | Kích thước chữ ký |
|---|---|---|---|
0x00 |
Không có chữ ký (không ký) | — | — |
0x01 |
ML-DSA-65 (FIPS 204) | 1.952 byte | 3.309 byte |
Người xem PHẢI từ chối các giá trị sig_alg không xác định.
9.3 Xây dựng tiền ảnh đã ký
Đã tồn tại hai phiên bản tiền ảnh. Mọi chữ ký PHẢI dùng V2, và các trình xác minh PHẢI chỉ chấp nhận V2. Tiền ảnh V1 cũ (được ghi lại bên dưới để tham khảo lịch sử) từng được chấp nhận như một phương án dự phòng chỉ dùng để xác minh trong quá trình di chuyển sang V2; phương án dự phòng đó đã bị loại bỏ và một chữ ký chỉ theo V1 nay bị từ chối.
V2 (hiện hành — được tạo bởi mọi hoạt động ký tác giả mới, và bởi cả hai chữ ký của luồng staging / đồng ký giao ước):
sig_input = SHA3-256(
"QUB_AUTHOR_SIG_V2" || // domain separator (17 bytes)
version || // u8 (1 byte)
qub_id || // [u8; 32] (32 bytes)
body_hash || // [u8; 32] (32 bytes)
unlock_at || // i64 big-endian (8 bytes)
0x00 || // u8 (1 byte): MUST be 0x00 in v1.x
sender_label_hash || // [u8; 32]: SHA3-256(NFC(sender_label)),
// or 32 zero bytes when absent
reply_to_or_zero // [u8; 32]: parent qub_id, or 32 zero
// bytes when absent
)
// Total preimage: 155 bytes → 32-byte hash
signature = Sign(author_secret_key, sig_input)
sender_label_hash tuân theo cùng quy ước giá trị vắng-mặt (absent-sentinel) như title_hash (§4.2.1): 32 byte 0 không phải là một đầu ra SHA3-256 hợp lệ, nên trạng thái "vắng mặt" không bao giờ có thể trùng với một nhãn hiện diện. Mọi trường đều có độ rộng cố định, nên tiền ảnh là rõ ràng mà không cần tiền tố độ dài.
V1 (cũ — ĐÃ LOẠI BỎ; không còn được tạo ra và không còn được chấp nhận khi xác minh):
sig_input = SHA3-256(
"QUB_AUTHOR_SIG_V1" || // domain separator (17 bytes)
version || // u8 (1 byte)
qub_id || // [u8; 32] (32 bytes)
body_hash || // [u8; 32] (32 bytes)
unlock_at || // i64 big-endian (8 bytes)
0x00 // u8 (1 byte): MUST be 0x00 in v1.0
)
// Total preimage: 91 bytes → 32-byte hash
Tiền ảnh V1 bỏ qua sender_label và reply_to. Nó từng được chấp nhận như một phương án dự phòng chỉ dùng để xác minh trong quá trình di chuyển sang V2; phương án dự phòng đó nay đã bị loại bỏ — các trình xác minh PHẢI chỉ chấp nhận tiền ảnh V2. Định nghĩa được giữ lại ở đây để tham khảo lịch sử và để giải thích dấu phân cách miền bên dưới. Một chữ ký chỉ xác minh được với V1 PHẢI được coi là một lỗi xác minh.
Dấu phân cách miền: "QUB_AUTHOR_SIG_V1" / "QUB_AUTHOR_SIG_V2" mỗi cái là 17 byte ASCII ([0x51, 0x55, 0x42, 0x5F, 0x41, 0x55, 0x54, 0x48, 0x4F, 0x52, 0x5F, 0x53, 0x49, 0x47, 0x5F, 0x56, 0x31/0x32]). Không có byte đệm. Dấu phân cách khác nhau tách miền hai cấu trúc, nên một chữ ký trên một tiền ảnh không bao giờ có thể xác minh được như tiền ảnh kia.
Byte org_id_present: byte theo sau unlock_at PHẢI là 0x00. Triển khai tham chiếu hiển thị điều này dưới dạng hằng số ORG_ID_PRESENT_INDIVIDUAL = 0x00 trong crates/qub-core/src/signing.rs; người xem tái tạo sig_input để xác minh PHẢI phát ra cùng một byte.
Phạm vi chữ ký — những gì được và không được bao phủ. sig_input V2 cam kết trực tiếp với version, qub_id, body_hash, unlock_at, sender_label, và reply_to (cộng với dấu phân cách miền cố định và byte org_id_present). qub_id chính nó được suy dẫn từ version, content_type, created_at, unlock_at, outcome_at, drand_round, và body_hash thông qua tiền ảnh §4.1, vì vậy bất kỳ thay đổi nào đối với các trường đó đều tạo ra một qub_id khác và làm mất hiệu lực chữ ký một cách bắc cầu. Do đó bề mặt được xác thực là:
| Trường | Được xác thực bởi chữ ký | Cách thức |
|---|---|---|
version |
✓ | Đầu vào trực tiếp vào sig_input |
qub_id |
✓ | Đầu vào trực tiếp |
body_hash |
✓ | Đầu vào trực tiếp |
unlock_at |
✓ | Đầu vào trực tiếp |
sender_label |
✓ | Đầu vào trực tiếp qua sender_label_hash (tiền ảnh V2 — hình thức duy nhất được chấp nhận) |
reply_to |
✓ | Đầu vào trực tiếp qua reply_to_or_zero (tiền ảnh V2 — hình thức duy nhất được chấp nhận) |
content_type |
✓ | Bắc cầu, qua tiền ảnh qub_id |
created_at |
✓ | Bắc cầu, qua tiền ảnh qub_id |
outcome_at |
✓ | Bắc cầu, qua tiền ảnh qub_id |
drand_round |
✓ | Bắc cầu, qua tiền ảnh qub_id (V1.2) |
body |
✓ | Bắc cầu, qua body_hash = SHA3-256(body) |
author_pubkey |
— (ngầm định) | Khóa đã xác minh chữ ký chính là tác giả, theo định nghĩa |
cosigner_pubkey / cosigner_signature |
— | Được ký độc lập trên cùng sig_input (xem §9.7) |
drand_chain_id, tlock_ciphertext, visibility |
— | Các trường SealedQub bên ngoài, không nằm trong envelope — được bao phủ bởi các bất biến cấu trúc riêng của chúng (tính nhất quán vòng / chuỗi) nhưng không bởi chữ ký tác giả. (drand_round hiện được liên kết một cách bắc cầu qua tiền ảnh qub_id — xem ở trên.) |
Vì sao V2 là tiền ảnh duy nhất được chấp nhận.
- Với tiền ảnh V1 đã bị loại bỏ, một bên có quyền ghi vào các byte đã lưu trữ có thể đổi
sender_label("Alice" → "Mallory") hoặc gắn lại cha củareply_to— và mã hóa lại sau vòng — mà không làm mất hiệu lực chữ ký tác giả, vì cả hai trường đều không nằm trong tiền ảnh đã ký. V2 bao phủ cả hai, nên bất kỳ thay đổi nào đối với một trong hai trường đều lật việc xác minh sang "thất bại". Vì các trình xác minh nay chỉ chấp nhận V2, việc đánh tráo này bị đóng lại đối với mọi chữ ký: một chữ ký không ràng buộc trường nào (tức là chỉ xác minh được với V1) bị từ chối thẳng thay vì bị hạ cấp xuống. author_pubkeybên trong envelope vẫn là điểm neo danh tính thực sự — người xem PHẢI suy dẫn danh tính hiển thị từauthor_pubkey(thông qua lớp chứng thực §9.5) thay vì tin tưởngsender_label.
Các triển khai hiển thị sender_label hoặc reply_to cho người dùng cuối PHẢI hiển thị danh tính đã xác thực (vân tay khóa công khai, chứng thực) làm tín hiệu danh tính chính, không phải nhãn.
9.4 Quy trình xác minh
1. Read sig_alg from QubEnvelope.
2. If sig_alg == 0x00 → unsigned. No verification. Display "unsigned qub."
3. If sig_alg is unknown → reject. Display "unrecognised signature scheme."
4. Extract author_signature and author_pubkey. If either is absent → integrity error.
5. Reconstruct sig_input using fields from QubEnvelope (V2 formula, §9.3).
6. Verify(author_pubkey, sig_input, author_signature). The V2 preimage is the
only accepted form — the legacy V1 fallback is retired (§9.3), so a
signature that does not verify against V2 fails, full stop.
7. If verification succeeds → display "signed by [key fingerprint]."
8. If verification fails → display "signature verification failed."
Xác minh chữ ký là thao tác đắt nhất (đặc biệt là ML-DSA-65). Nó NÊN được thực hiện sau khi tất cả các kiểm tra rẻ hơn (hash, qub_id, unlock_at) đã đạt.
9.5 Chứng thực danh tính
Chứng thực danh tính — ánh xạ author_pubkey đến các tuyên bố danh tính có thể nhận biết bởi con người như handle qub, địa chỉ email, handle mạng xã hội, hoặc thông tin xác thực passkey — là một cải tiến tiến bộ phía người xem và không bắt buộc cho việc xác minh chữ ký. Người xem phân giải chứng thực thành một danh tính hiển thị PHẢI áp dụng thứ tự ưu tiên:
handle > email > social > fingerprint
Dấu vân tay dự phòng là hex chữ thường của SHA3-256(author_pubkey); nó luôn có sẵn cho bất kỳ qub đã ký nào. Người xem CÓ THỂ rút gọn nó để hiển thị — trình xem tham chiếu hiển thị qub: theo sau là bốn byte đầu và bốn byte cuối (qub:<8 hex>…<8 hex>).
Một trình xác minh tuân thủ có thể hoàn thành mọi kiểm tra trong §9.4 mà không cần liên hệ với API qub, không cần mạng nào ngoài nơi lưu trữ vĩnh viễn và drand, và không cần tra cứu phía máy chủ. Phân giải chứng thực là một bước nỗ lực tốt nhất riêng biệt chỉ được thực hiện sau khi việc xác minh chữ ký đã thành công.
9.6 Tác động kích thước
| Ed25519 | ML-DSA-65 | |
|---|---|---|
| Chữ ký | 64 byte | 3.309 byte |
| Khóa công khai | 32 byte | 1.952 byte |
| Tổng cộng cho mỗi qub | 96 byte | 5.261 byte |
| Chênh lệch chi phí lưu trữ (ở mức ~$5/MB) | ~$0.0005 | ~$0.026 |
Đối với một qub văn bản 500–2.000 byte, ML-DSA-65 tăng kích thước lưu trữ lên khoảng ba lần. Chi phí tuyệt đối là không đáng kể.
9.7 Xác minh người đồng ký (thỏa thuận song phương giao ước)
Đối với các thỏa thuận song phương (content_type = 0x03), một lớp chữ ký thứ hai chứng minh cả hai bên đã đồng ý với các điều khoản giống nhau.
Các trường envelope:
cosigner_pubkey: Khóa công khai ML-DSA-65 của người đồng ký (Bên B).cosigner_signature: Chữ ký trên cùngsig_inputnhư tác giả (§9.3).
Cả hai trường PHẢI hiện diện cùng nhau hoặc cả hai vắng mặt. Nếu chính xác một trường hiện diện, người xem PHẢI báo cáo lỗi toàn vẹn.
Quy trình xác minh:
1. If cosigner_pubkey absent and cosigner_signature absent → no cosigner. Done.
2. If exactly one is present → integrity error.
3. Verify cosigner_pubkey != author_pubkey (prevent self-cosigning).
Fail → display "cosigner pubkey must differ from author."
4. Reconstruct sig_input using the same formula as §9.3 (V2 only — the
legacy V1 fallback is retired; all pact clients produce V2 signatures).
5. Verify(cosigner_pubkey, sig_input, cosigner_signature).
6. Success → display "co-signed by [cosigner fingerprint]."
7. Failure → display "co-signature verification failed."
Tính chất:
- Người đồng ký ký cùng
sig_inputgiống hệt như tác giả — cả hai bên cam kết với cùngqub_id,body_hash, vàunlock_at(và, theo V2, cùngsender_label_hashvàreply_to_or_zero). - Để cho phép người đồng ký tái tạo tiền ảnh V2 mà không cần truy cập các byte envelope thô, dịch vụ staging thực thi tại thời điểm stage rằng
sender_labelcủa một envelope giao ước bằngpact_terms.party_a.labelvà rằngreply_tovắng mặt. Cả hai điều kiện đều đúng với mọi giao ước của ứng dụng tham chiếu; các envelope vi phạm bị từ chối tại staging. - Việc suy dẫn
qub_id(§4.1) KHÔNG bao gồm các trường người đồng ký. Thêm một người đồng ký vào một envelope hiện có không thay đổiqub_id. - Một giao ước có thể được ký chỉ bởi tác giả (cam kết một chiều), chỉ bởi người đồng ký (bất thường), hoặc cả hai (chứng minh song phương đầy đủ).
Cổng ràng buộc email (vận hành). Khi một giao ước đã staging mang liên hệ email Bên B (§6.1), dịch vụ tải lên qub PHẢI từ chối yêu cầu đồng ký trừ khi tồn tại một dấu hiệu xác minh email tồn tại ngắn khớp với cả id staging và mã băm email đã chuẩn hóa của liên hệ đó. Dấu hiệu được ghi bởi /api/v1/auth/verify khi mã thông báo magic-link mang một staging_id và địa chỉ đã xác minh khớp với SHA-256(normalise_email(party_b.contact)) — trong đó normalise_email(addr) bảo toàn chữ hoa chữ thường của phần local và chỉ chuyển phần domain thành chữ thường (theo RFC 5321 §2.3.11), và SHA-256 ở đây là hàm băm NIST FIPS 180-4 (khác với SHA3-256 được sử dụng trong các suy dẫn §4) — và hết hạn 900 giây (15 phút) sau khi cấp. Đây là một cổng vận hành chống mạo danh, KHÔNG phải là một phần của bằng chứng qub trên chuỗi — một trình xác minh bên thứ ba phát lại §11 chỉ cần nơi lưu trữ vĩnh viễn và drand, không cần bất kỳ tra cứu phía máy chủ nào. Dấu hiệu chỉ tồn tại phía máy chủ và không bao giờ là một phần của thân được ký.
Tác động kích thước (tác giả ML-DSA-65 + người đồng ký):
| Thành phần | Kích thước |
|---|---|
| Chữ ký tác giả | 3.309 byte |
| Khóa công khai tác giả | 1.952 byte |
| Chữ ký người đồng ký | 3.309 byte |
| Khóa công khai người đồng ký | 1.952 byte |
| Tổng chi phí mật mã | 10.522 byte |
| Chênh lệch chi phí lưu trữ | ~$0.05 |
10. Hiển thị và làm sạch Markdown
Phần này quan trọng về mặt bảo mật. Người xem hiển thị các qub văn bản (content_type = 0x01) sử dụng một tập hợp con Markdown bị hạn chế.
10.1 Các phần tử được phép
- Tiêu đề:
#đến####(không#####hoặc######) - Nhấn mạnh: in đậm (
**), in nghiêng (*), gạch ngang (~~) - Danh sách: có thứ tự (
1.) và không có thứ tự (-,*) - Khối trích dẫn (
>) - Mã: span nội tuyến (```) và khối có rào (`````)
- Đường ngang (
---) - Ngắt dòng (hai khoảng trắng cuối hoặc dòng trống)
- Đoạn văn
10.2 Các phần tử bị cấm
| Phần tử | Xử lý |
|---|---|
HTML thô (<div>, <script>, v.v.) |
Loại bỏ hoàn toàn. Không có HTML nào đi qua. |
Hình ảnh () |
Loại bỏ. Cú pháp hình ảnh bị xóa khỏi đầu ra. |
Liên kết ([text](url)) |
URL được hiển thị dưới dạng văn bản thuần nhìn thấy. Không tự động liên kết. Không thể nhấp mà không có hành động rõ ràng từ người dùng. |
| Các sơ đồ URL nguy hiểm | javascript:, data:, vbscript:, file: — loại bỏ. |
| Iframe, embed, object | Loại bỏ. |
| Thực thể HTML | Chỉ giải mã thành ký tự hiển thị nếu an toàn. |
10.3 Triển khai
Các triển khai PHẢI sử dụng bộ phân tích danh sách cho phép nghiêm ngặt, không phải danh sách chặn. Cách tiếp cận được khuyến nghị:
- Phân tích Markdown bằng
pulldown-cmark(hoặc tương đương). - Duyệt AST và bỏ bất kỳ node nào không có trong danh sách cho phép (§10.1).
- Đối với các node liên kết: phát URL dưới dạng văn bản nhìn thấy, không phải dưới dạng phần tử
<a>có thể nhấp. - Chuyển đổi AST đã lọc thành một biểu diễn trung gian có kiểu (ví dụ, một enum
MarkdownNodechỉ có các biến thể an toàn). HTML thô về mặt cấu trúc không thể biểu diễn được trong IR này. - Hiển thị từ IR có kiểu đến lớp view đích (ví dụ, các thành phần view phản ứng, các node DOM). Không có sự ghép chuỗi HTML hoặc
innerHTMLtại bất kỳ thời điểm nào.
Các cách tiếp cận danh sách chặn rất mong manh vì các phần mở rộng Markdown mới hoặc các đặc tính riêng của bộ phân tích có thể đưa vào các phần tử không được lọc. Cách tiếp cận AST có kiểu làm cho XSS về mặt cấu trúc là không thể — không có biến thể nào có thể mang HTML tùy ý.
10.4 Giới hạn kích thước và cấu trúc
- Độ sâu tiêu đề hiển thị tối đa:
####(H4).#####và sâu hơn được hiển thị dưới dạng văn bản in đậm. - Không có giới hạn về số đoạn văn (giới hạn kích thước thân trong §6 là ràng buộc).
- Khối mã có rào: không có làm nổi bật cú pháp trong MVP. Hiển thị dưới dạng văn bản định dạng sẵn đơn cách.
11. Xác minh bên thứ ba
Bất kỳ bên thứ ba nào cũng có thể xác minh một qub công khai mà không cần sự hợp tác của qub. Quy trình xác minh:
1. Obtain arweave_tx_id (from delivery URL or direct knowledge).
2. Fetch SealedQubCbor from any storage gateway.
3. Confirm storage block inclusion (block height, block timestamp).
4. Parse SealedQubCbor → SealedQub.
5. Fetch drand round signature for SealedQub.drand_round.
6. tlock_decrypt(tlock_ciphertext, round_signature) → QubEnvelope CBOR bytes.
7. Parse → QubEnvelope.
8. Verify SHA3-256(body) == body_hash.
9. Verify QubEnvelope.qub_id == SealedQub.qub_id.
10. Verify QubEnvelope.unlock_at == SealedQub.unlock_at.
11. If sig_alg != 0x00: verify author_signature (see §9.4).
12. All checks pass → qub is verified.
Những gì xác minh chứng minh:
| Bằng chứng | Điều nó thiết lập |
|---|---|
| Cam kết | Ciphertext đã tồn tại trước dấu thời gian khối lưu trữ. |
| Toàn vẹn | Thân văn bản thuần khớp với mã băm đã cam kết và chưa bị thay đổi. |
| Định thời | Nội dung không thể đọc được cho đến vòng drand, tương ứng với thời gian mở khóa đã chọn (tùy thuộc vào các giả định bảo mật của tlock và drand). |
Những gì xác minh KHÔNG chứng minh:
| Không-chứng minh | Lý do |
|---|---|
| Quyền tác giả | sender_label chỉ để trang trí. Không có sig_alg ≥ 0x01, bất kỳ ai cũng có thể đã niêm phong nội dung này. |
| Ý định | qub chứng minh nội dung và định thời, không phải những gì người tạo có ý nghĩa chủ quan. |
| Định thời trước sự kiện | Việc đưa vào khối lưu trữ có thể chậm hơn so với việc tải lên thực tế vài phút. Dấu thời gian cam kết là thời gian khối, không phải thời điểm người dùng nhấn "niêm phong." |
12. Phiên bản hóa
12.1 Phiên bản giao thức
Trường version (u8) trong cả SealedQub và QubEnvelope xác định phiên bản chính của giao thức.
- Người xem PHẢI từ chối các phiên bản chính không xác định kèm theo lỗi rõ ràng.
- Trong một phiên bản chính đã biết, các bộ giải mã PHẢI từ chối các khóa map không xác định (§3.1) — việc tiến hóa schema diễn ra bằng cách giới thiệu một
versionmới, không phải bằng cách thêm các khóa mà các bộ giải mã hiện có sẽ bỏ qua. (Các bản sửa đổi trước của đặc tả này cho phép tha thứ các trường tùy chọn không xác định; điều khoản đó đã được rút lại — nó làm choencode(decode(x))không còn đơn ánh và mở ra một vector tấn công nội-dung-được-ký-ẩn trên các tải trọng giao ước.) - Các loại nội dung (
content_type) và các sơ đồ chữ ký (sig_alg) được kiểm soát bởi phiên bản: các giá trị mới chỉ có thể được giới thiệu cùng với một phiên bản giao thức mới hoặc cập nhật sổ đăng ký rõ ràng.
12.2 Lịch sử phiên bản
| Phiên bản | Giá trị | Mô tả |
|---|---|---|
| v1 | 0x01 |
Các qub văn bản công khai (content_type 0x01), các thỏa thuận song phương giao ước (0x03, schema structured/v1, tác giả ML-DSA-65 + người đồng ký), tlock, SHA3-256 |
12.3 Tương thích thuận
Một người xem v1 gặp một QubEnvelope với các khóa map CBOR không xác định (các khóa không trong thứ tự chuẩn §3.2) PHẢI từ chối nó kèm một lỗi giải mã (§3.1). Tính tương thích thuận dựa vào trường version, không phải vào sự tha thứ khóa: các bổ sung trong tương lai — kể cả siêu dữ liệu nhỏ — được phát hành dưới một giá trị version mới, mà một người xem v1 từ chối kèm một lỗi "giao thức mới hơn" rõ ràng thay vì lẳng lặng bỏ đi nội dung mà các chữ ký cam kết.
Một người xem v1 gặp sig_alg = 0x01 (ML-DSA-65) nhưng thiếu hỗ trợ xác minh ML-DSA-65 NÊN hiển thị nội dung qub với thông báo "chữ ký hiện diện nhưng không thể xác minh", không từ chối qub hoàn toàn. Triển khai tham chiếu hiện tại từ chối mọi giá trị sig_alg ngoài 0x00 và 0x01 vì sổ đăng ký v1 không chứa thuật toán hợp lệ nào khác — từ chối nghiêm ngặt và soft-fail là không thể phân biệt được về mặt quan sát cho đến khi một thuật toán thứ ba được đăng ký. Hành vi soft-fail ở trên trở nên quan trọng một khi §9.2 chấp nhận một mục mới, và người xem tham chiếu sẽ được cập nhật để soft-fail tại thời điểm đó.
12.4 Phiên bản bao bọc bên ngoài
OuterWrapper được mô tả trong §13 mang byte version riêng của nó, độc lập với SealedQub.version và QubEnvelope.version. Hai không gian phiên bản phát triển riêng biệt: một thay thế đối xứng an toàn hậu lượng tử trong tương lai sẽ tăng byte bao bọc mà không động đến phiên bản giao thức bên trong, và một bổ sung ở lớp giao thức trong tương lai (ví dụ, một trường envelope mới) tăng phiên bản bên trong mà không động đến byte bao bọc.
OUTER_WRAPPER_VERSION_* |
Giá trị | Thuật toán | Trạng thái |
|---|---|---|---|
OUTER_WRAPPER_VERSION_1 |
0x01 |
AES-256-GCM với nonce 12 byte, thẻ xác thực 16 byte, AAD ràng buộc với qub_id |
mặc định v1 |
| — | 0x02–0xFF |
Dành riêng | Tương lai |
Người xem PHẢI từ chối các phiên bản bao bọc không xác định kèm theo lỗi rõ ràng. Giao thức cố ý giữ không gian phiên bản bao bọc hẹp cho đến khi một động cơ di chuyển cụ thể xuất hiện (ví dụ, hướng dẫn NIST ưa chuộng một AEAD khác); một khe 0x02 sẽ được phân bổ trong cùng bản sửa đổi giới thiệu thuật toán.
13. Lớp bao bọc mã hóa bên ngoài
13.1 Lý do căn bản
Các lớp giao thức (QubEnvelope → tlock → SealedQub) làm cho một qub đã niêm phong bị khóa thời gian: thân không thể đọc được cho đến unlock_at và chữ ký vòng drand đã được công bố. Tuy nhiên, sau khi mở khóa, chữ ký vòng là công khai và hình dạng CBOR chuẩn của SealedQub có thể nhận diện được, vì vậy một người thu thập đã lập chỉ mục các giao dịch lưu trữ vĩnh viễn có thể giải mã hàng loạt toàn bộ kho qub.
Lớp bao bọc mã hóa bên ngoài đóng kênh đó bằng cách chèn một lớp AEAD đối xứng bổ sung giữa SealedQubCbor chuẩn và các byte được ghi vào nơi lưu trữ vĩnh viễn. Khóa 256 bit K chỉ tồn tại trong đoạn fragment URL của liên kết bàn giao và trên các thiết bị của người dùng; các trình duyệt không truyền các fragment URL đến các máy chủ, vì vậy qub.social, mọi cổng lưu trữ, và mọi CDN ở phía trước của một trong hai đều mù quan sát đối với K. Do đó, mỗi qub trong nơi lưu trữ vĩnh viễn là một ciphertext mờ mà bản rõ không thể khôi phục được mà không có URL mà người tạo đã chọn để chia sẻ.
Hiệu ứng ròng:
- Miễn nhiễm liệt kê theo mặc định. Các byte đã bao bọc trong nơi lưu trữ vĩnh viễn là không thể phân biệt theo byte với ciphertext tùy ý. Một chiến lược thu thập "truy vấn GraphQL cho các bản tải lên có hình dạng qub, giải mã hàng loạt bằng các chữ ký drand công khai" không kết thúc với văn bản thuần.
- Thế bảo mật crypto-shredding. qub.social theo nghĩa đen không thể giải mã kho của chính nó. Trát đòi tới chiphertext, không phải bản rõ.
- Thang bảo mật hai cấp. Mặc định = truy cập kiểm soát bằng liên kết (phần này). Các qub riêng tư được mã hóa cho người nhận (một tính năng Giai đoạn 2 dành riêng, chưa được đặc tả) chồng lên trên dưới dạng cấp thứ hai.
13.2 Phân lớp
plaintext body ← QubEnvelope.body (§2.2)
↓ canonical CBOR (§3)
envelope CBOR
↓ tlock encrypt to drand round (§7 step 10)
tlock_ciphertext (inside SealedQub) (§2.3)
↓ canonical CBOR (§3)
SealedQubCbor bytes ← inner wire artifact
↓ AES-256-GCM(K, nonce, AAD=qub_id) (§7 step 12a, this section)
OuterWrapper CBOR bytes ← uploaded to permanent storage (§7 step 15)
Niêm phong và mở khóa ở lớp giao thức (§7, §8) không thay đổi bên dưới ranh giới của lớp bao bọc; lớp bao bọc gắn vào tại điểm gọi của seal() và tách ra tại điểm gọi của unlock().
13.3 Cấu trúc dữ liệu OuterWrapper
struct OuterWrapper {
version: u8, // 0x01, see §12.4
qub_id: [u8; 32], // copied from inner SealedQub; AEAD AAD
nonce: [u8; 12], // 96-bit AEAD nonce
ciphertext: Vec<u8>, // AES-256-GCM(K, nonce, SealedQubCbor, AAD=qub_id) || 16-byte tag
}
Các bất biến của trường.
versionPHẢI bằng0x01cho các byte bao bọc v1.0.qub_idPHẢI bằng trườngqub_idcủa SealedQub được khôi phục sau khi tháo bao bọc. Bước tháo bao bọc không thực thi điều này trực tiếp (sự ràng buộc AEAD AAD làm cho việc giả mạo ở cấp byte là không thể), nhưng lớp mở khóa kiểm tra mối quan hệ một cách bắc cầu: nếu một người tạo bao bọc mộtSealedQubCbormàqub_idbên trong của nó không khớp vớiqub_idcủa lớp bao bọc, §8 bước 11 sẽ thất bại.noncePHẢI là 96 bit (12 byte), được tạo mới bởi một CSPRNG cho mỗi thao tác bao bọc. Tái sử dụng một nonce dưới cùng một khóa cho phép các cuộc tấn công tái sử dụng nonce AEAD khôi phục bản rõ; các nhà sản xuất PHẢI xử lý các cặp (key,nonce) như một-lần.ciphertextlà đầu ra AES-256-GCM: các byte ciphertext được nối với thẻ xác thực 16 byte.ciphertext.len() == SealedQubCbor.len() + 16chính xác.
Mã hóa CBOR. CBOR chuẩn theo §3, với cùng quy tắc thứ tự khóa (sắp xếp theo độ dài byte đã mã hóa tăng dần, sau đó theo từ điển). Bốn khóa là:
| Khóa | Byte đã mã hóa | Thứ tự |
|---|---|---|
nonce |
6 | 1 |
qub_id |
7 | 2 |
version |
8 | 3 |
ciphertext |
11 | 4 |
Do đó byte đầu tiên của CBOR OuterWrapper là header map độ dài xác định cho một map 4 mục (0xA4).
13.4 Ràng buộc AAD với qub_id
Lớp bao bọc ràng buộc qub_id làm dữ liệu được xác thực bổ sung AEAD. Đây là phòng thủ cấu trúc quan trọng chống lại ba lớp tấn công:
| Tấn công | Phòng thủ |
|---|---|
Di chuyển ciphertext dưới một trường qub_id khác trong lớp bao bọc |
AAD không khớp → xác thực AEAD thất bại |
| Trộn fragment URL của qub A với các byte lưu trữ vĩnh viễn của qub B | AAD không khớp → xác thực AEAD thất bại |
Giả mạo trường qub_id của lớp bao bọc sau khi tải lên |
AAD không khớp → xác thực AEAD thất bại |
Việc mang qub_id trong văn bản thuần của lớp bao bọc không làm suy yếu miễn nhiễm liệt kê đáng kể — qub_id chính nó là một hàm băm SHA3-256 của tiền ảnh §4.1 không có tiền ảnh có thể khôi phục được từ bản tóm tắt, và một người liệt kê đã thu thập các byte của lớp bao bọc không học được gì từ qub_id nhìn thấy được mà họ không thể suy luận từ chính sự tồn tại của bản tải lên.
13.5 Thuật toán bao bọc và tháo bao bọc
wrap_sealed_qub(SealedQubCbor S, qub_id Q, key K, nonce N):
require K.len() == 32 and N.len() == 12 and Q.len() == 32
C := AES_256_GCM_encrypt(key=K, nonce=N, msg=S, aad=Q)
// C includes the 16-byte authentication tag at the end
return canonical_cbor_encode(OuterWrapper{
version: 0x01,
qub_id: Q,
nonce: N,
ciphertext: C,
})
unwrap_sealed_qub(OuterWrapper bytes W, key K):
require K.len() == 32
O := canonical_cbor_decode(W) as OuterWrapper
require O.version == 0x01 // §12.4
P := AES_256_GCM_decrypt(
key=K, nonce=O.nonce, ciphertext=O.ciphertext, aad=O.qub_id
)
// any AEAD failure → DECRYPT_FAILED, indistinguishable to caller
return P // P is the inner SealedQubCbor
Sự sụp đổ chế độ thất bại. K sai, nonce sai, AAD không khớp, và ciphertext bị giả mạo đều tạo ra cùng một lỗi DECRYPT_FAILED. Đây là một tính chất AEAD có chủ đích: phân biệt chế độ thất bại sẽ tạo ra một kênh phụ mà kẻ tấn công từ xa có thể dò bằng cách gửi các lớp bao bọc bị lỗi và đo thời gian phản hồi. Các triển khai tham chiếu PHẢI gộp tất cả các thất bại AEAD thành một hình dạng lỗi duy nhất.
13.6 Vật liệu khóa và phân phối
Khóa bao bọc K là một giá trị ngẫu nhiên đều 256 bit được tạo cho mỗi qub bởi một CSPRNG. Các triển khai tham chiếu lấy nó từ:
- WASM người tạo:
getrandom(WebCrypto dưới backendwasm_js). - Bên gọi API niêm phong phía máy chủ: CSPRNG cục bộ của nó; bên gọi cung cấp và giữ lại
Kdưới dạngwrapper_key_b64url. Worker dùngKtrong bộ nhớ cho lớp bao bọc nhưng KHÔNG ĐƯỢC lưu giữ nó. Điều này cho phép một lần thử lại idempotent khôi phục một phản hồi đã bị lược bỏ bằng năng lực được giữ lại của bên gọi thay vì phụ thuộc vào một bí mật dùng một lần do máy chủ tạo ra.
Phân phối: K PHẢI được mã hóa dưới dạng base64 an toàn cho URL (RFC 4648 §5, không đệm) và được nối vào URL bàn giao dưới dạng thành phần fragment:
delivery_url = <origin>/c/<arweave_tx_id>#<base64url(K)>
Fragment không bao giờ được truyền đến bất kỳ máy chủ nào bởi một trình duyệt tuân thủ. Các kênh khôi phục (chỉ mục lịch sử phía máy chủ, tự động gửi email chọn tham gia) duy trì URL bàn giao đầy đủ — bao gồm fragment — vượt ra ngoài thiết bị của người dùng là một sự đánh đổi rõ ràng chống lại thế crypto-shredding mặc định và PHẢI được kiểm soát bằng sự đồng ý rõ ràng của người dùng.
Mất fragment. Nếu một người dùng mất fragment URL và không có kênh khôi phục, qub không thể đọc được. Đây là sự đánh đổi quan trọng của thiết kế và PHẢI được công bố cho người dùng tại thời điểm niêm phong. MVP củng cố công bố tại thời điểm niêm phong với văn bản "lưu URL này" rõ ràng và một kênh khôi phục email đã xác minh cho những người dùng chọn tham gia.
13.7 Ngoài phạm vi cho phần này
- Ký quyền tác giả (§9) không thay đổi: các chữ ký được tính toán bên trong
QubEnvelopebên trong và được khôi phục sau khi tháo bao bọc → giải mã tlock → phân tích CBOR. - Các qub riêng tư được mã hóa cho người nhận (một tính năng Giai đoạn 2 dành riêng, chưa được đặc tả) ghép trên lớp bao bọc này dưới dạng cấp bảo mật thứ hai; cả hai cấp đều có thể hoạt động đồng thời.
- Các giao ước (§6, content_type
0x03) được bao bọc chính xác như các qub văn bản; lớp bao bọc mù về byte đối với loại nội dung bên trong.
13.8 Các qub công khai (lược bỏ lớp bao bọc)
Lớp bao bọc bên ngoài là tùy chọn ở lớp bàn giao. Một người tạo có thể niêm phong một qub dưới dạng công khai, trong trường hợp đó SealedQubCbor chuẩn được ghi vào nơi lưu trữ vĩnh viễn trực tiếp, không có lớp OuterWrapper và không có khóa K:
SealedQubCbor bytes ──(public)──▶ uploaded to permanent storage as-is
SealedQubCbor bytes ──(private)─▶ AES-256-GCM(K, …) ▶ OuterWrapper ▶ uploaded
Một qub công khai là bị khóa thời gian nhưng không bị kiểm soát bằng liên kết: nó vẫn không thể đọc được cho đến khi vòng drand của nó được công bố (lớp tlock không thay đổi), nhưng sau khi mở khóa, bất kỳ ai có arweave_tx_id đều có thể giải mã nó — không cần fragment URL, vì không có K. Đây là sự đánh đổi có chủ đích cho các bề mặt mà máy chủ phải điều khiển: email thông báo công bố, các nhúng của bên thứ ba, và SEO sau-công-bố phong phú hơn đều cần một liên kết hoạt động được mà không có một bí mật mà máy chủ không bao giờ nắm giữ (§13.6).
Các hệ quả mà một nhà sản xuất PHẢI tính đến:
- Không có miễn nhiễm liệt kê. Các qub công khai từ bỏ tính chất miễn nhiễm liệt kê ở §13.1 theo cấu trúc. Dịch vụ tải lên tham chiếu đóng dấu một tag lưu trữ vĩnh viễn
Visibility: publiclên chúng (và chỉ chúng) để chúng có thể được khám phá một cách cố ý; các qub riêng tư không mang tag như vậy và giữ tính không thể phân biệt theo byte của chúng. - Tiêu đề bản rõ bị phơi bày tại thời điểm niêm phong. Trường
titleở §3.2 là bản rõ bên trongSealedQubCbor. Dưới lớp bao bọc, nó được ẩn cho đến khi một người xem cung cấpK; không có lớp bao bọc, nó có thể đọc được bởi cả thế giới trên nơi lưu trữ vĩnh viễn kể từ thời điểm tải lên, trước khi mở khóa. Các ứng dụng người tạo tuân thủ PHẢI công bố điều này tại thời điểm niêm phong. - Việc phát hiện mang tính cấu trúc. Một người xem/nhúng tuân thủ phân biệt hai hình dạng bằng cách phân tích: các byte phân tích được dưới dạng
OuterWrapperđi theo đường tháo-bao-bọc-bằng-K; các byte phân tích được dưới dạng mộtSealedQubCbortrần được chấp nhận trực tiếp. Không cần cờ trên wire, vàqub_idkhông ràng buộc khả năng nhìn thấy — cùng một nội dung là giống hệt theo byte ở lớpSealedQubbất kể được niêm phong công khai hay riêng tư.
Riêng tư (đã bao bọc) vẫn là mặc định; công khai là một lựa chọn rõ ràng của người tạo trên mỗi qub.
14. Các vector kiểm tra
14.1 Suy dẫn qub_id
Input:
version = 0x01
content_type = 0x01
created_at = 1735689600 (2025-01-01 00:00:00 UTC)
unlock_at = 1736294400 (2025-01-08 00:00:00 UTC)
outcome_at = absent
drand_round = 4695445 (= (1736294400 - 1595431050) / 30, drand mainnet params §14.2)
body = "Hello, future." (UTF-8, 14 bytes)
title = absent
Intermediate:
body_hash = SHA3-256("Hello, future.")
= 76ab8b3f843c6ed4f2d0fd75b9f457b4
ad49dd4450f9c22723ae430e3af3211d
title_hash = [0u8; 32] (title absent — §4.2.1 sentinel)
Domain separator (10 bytes):
[0x51, 0x55, 0x42, 0x5F, 0x49, 0x44, 0x5F, 0x56, 0x32, 0x00]
Preimage (108 bytes — V1.2):
domain_separator || // 10 bytes
0x01 || // version
0x01 || // content_type
0x0000000067748580 || // created_at as i64 big-endian (1735689600)
0x00000000677DC000 || // unlock_at as i64 big-endian (1736294400)
0x0000000000000000 || // outcome_at_or_zero (outcome_at absent)
0x000000000047A595 || // drand_round as u64 big-endian (4695445)
body_hash || // 32 bytes
title_hash // 32 bytes (all-zeros sentinel; title absent)
Expected output:
qub_id = SHA3-256(preimage)
= 3a9fcb31b750d985c262fada6d4f777f
d6a28be831d941d85c131f5a4bbaf8a4
Các triển khai PHẢI tạo ra các giá trị body_hash và qub_id giống hệt cho đầu vào này. Vector kiểm tra này NÊN là bài kiểm tra đơn vị đầu tiên được viết. Các giá trị chuẩn ở trên được tính toán bởi triển khai tham chiếu và PHẢI khớp từng bit. Các bố cục tiền ảnh lịch sử (trước ra mắt — không có qub trực tiếp nào phụ thuộc vào chúng): qub_id V1.0 với tiền ảnh 92 byte là 3d9fc2390eab043d38a1669ed3b71be76f9eefe872b9569ab1aaa027b88392b0; qub_id V1.1 với tiền ảnh 100 byte (sau khi gấp outcome_at_or_zero) là b0d032898ad629795150fdcb3f84e518f59ed05b7a2a82bc24ebdb87f52144ed. V1.2 gấp drand_round vào và nâng dấu phân cách miền lên QUB_ID_V2.
14.2 Ánh xạ vòng mở khóa
Input:
unlock_at = 1735689600
chain_genesis_time = 1595431050
chain_period_seconds = 30
Calculation:
(1735689600 - 1595431050) / 30 = 4675285.0
ceil(4675285.0) = 4675285
drand_round = 4675285
14.3 Vòng lặp CBOR chuẩn
Các triển khai PHẢI xác minh rằng serialize(parse(serialize(qub))) == serialize(qub) cho tất cả các đầu vào hợp lệ. Đây là một kiểm tra thuộc tính, không phải một vector đơn lẻ.
14.4 CBOR PactTerms (content_type 0x03)
Input:
pact_version = 1
title = "Scooter deposit"
terms = [
{ key: "Item", value: "Honda Metropolitan scooter" },
{ key: "Price", value: "$100" },
{ key: "Deposit", value: "$10" }
]
party_a = { label: "Alice" }
party_b = { label: "Bob", contact: "bob@example.com" }
notes = absent
Canonical CBOR key order (PactTerms):
"notes"(6) < "terms"(6) < "title"(6) < "party_a"(8) < "party_b"(8) < "pact_version"(13)
Canonical CBOR key order (PactTerm):
"key"(4) < "value"(6)
Canonical CBOR key order (PartyIdentifier):
"label"(6) < "contact"(8)
Các byte CBOR chuẩn và body_hash SHA3-256 được tính toán bởi triển khai tham chiếu. Các triển khai PHẢI tạo ra CBOR giống hệt byte cho đầu vào này.
Các triển khai cũng PHẢI xác minh rằng serialize(parse(serialize(pact))) == serialize(pact) cho tất cả các đầu vào PactTerms hợp lệ (kiểm tra thuộc tính).
14.5 Các vector lớp bao bọc bên ngoài đa ngôn ngữ
Lớp bao bọc bên ngoài (§13) có một fixture chuẩn riêng tại crates/qub-core/tests/vectors/wrapper_v1.json. Mỗi trường hợp cố định một bộ (key, nonce, qub_id, sealed_cbor) làm đầu vào hex mờ và khẳng định một đầu ra expected_wrapper_hex cụ thể. Cả hai triển khai tham chiếu đều tiêu thụ cùng một file JSON:
- Rust:
crates/qub-core/tests/wrapper_vectors.rs(cargo test -p qub-core --test wrapper_vectors). - TypeScript:
workers/api/src/crypto/__tests__/wrapper.test.ts(npm test).
Fixture hiện tại ghim ba trường hợp:
| Trường hợp | Phạm vi bao phủ |
|---|---|
basic-text-public |
Hình dạng SealedQub thực tế nhỏ nhất; không có trường tùy chọn. Thiết lập hình dạng lớp bao bọc chuẩn cho một qub điển hình v1.0. |
with-recipient-pubkey |
SealedQub với recipient_pubkey được đặt (đường dẫn Giai đoạn 2). Bộ khóa CBOR bên trong khác nhau, qub_id khác nhau. |
longer-body |
Thân ~4 KiB — luyện tập các tiền tố độ dài CBOR đa byte bên trong cả envelope bên trong và ciphertext bên ngoài. |
Các triển khai PHẢI tạo ra expected_wrapper_hex giống hệt byte cho các đầu vào đã ghi. Việc tái tạo fixture yêu cầu QUB_REGEN_VECTORS=1 cargo test -p qub-core --test wrapper_vectors và được dành riêng cho các thay đổi định dạng có chủ ý.
15. Quản trị hồ sơ mật mã (Tương lai)
Phần này có tính thông tin cho v1 và trở thành quy phạm lần đầu tiên khi một thuật toán thứ hai bước vào bất kỳ nguyên thủy mật mã nào của qub.
15.1 Thế hiện tại
Giao thức v1 ràng buộc chính xác một thuật toán cho mỗi nguyên thủy:
- Chữ ký: ML-DSA-65 (
sig_alg = 0x01; khóa công khai 1952 byte, chữ ký 3309 byte) và không ký (sig_alg = 0x00). Sổ đăng ký §9.2 không định nghĩa giá trị nào khác; một trình xác minh v1 PHẢI từ chối mọisig_algngoài{0x00, 0x01}. Một mục Ed25519 trong tương lai được dự kiến (§15.3) nhưng chưa được cấp phát trong v1. - Khóa thời gian: chỉ drand quicknet — mã băm chuỗi, khóa công khai, thời gian khởi nguồn và chu kỳ là các tham số mạng cố định được mang bởi
DrandTimelockProvider::quicknet()tham chiếu (crates/qub-core/src/tlock.rs) vàconfig/drand-endpoints.json. - Lớp bao bọc bên ngoài: chỉ AES-256-GCM v1 (§13).
Các trình xác minh hiện tại cứng mã hóa độ dài khóa và chữ ký cho mỗi nguyên thủy. Không có bề mặt linh hoạt nào được hiển thị bởi định dạng wire.
15.2 Hình dạng dự định
Khi một thuật toán thứ hai bước vào giao thức, trình xác minh sẽ được cấu hình cho một CryptoProfile có tên (ví dụ, ExqubV1) liệt kê tập hợp chính xác các giá trị được phép cho mỗi nguyên thủy — sig_algs, các chuỗi drand, các phiên bản lớp bao bọc, các loại nội dung. Hồ sơ được cố định tại thời điểm xác minh, không bao giờ được thương lượng trong băng. Bất kỳ giá trị nào ngoài hồ sơ đang hoạt động đều bị từ chối.
Điều này đảm bảo rằng việc thêm ML-DSA-87 hoặc kích hoạt Ed25519 không thể làm suy yếu hồi tố các cấu hình trình xác minh hiện có: một trình xác minh v1 vẫn là một trình xác minh v1 ngay cả sau khi một hồ sơ v2 được công bố.
15.3 Điều kiện kích hoạt
Thăng cấp §15 lên trạng thái quy phạm khi bất kỳ điều nào sau đây được đề xuất:
- Một byte
sig_algthứ hai (kích hoạt Ed25519, ML-DSA-87, hoặc bất kỳ mục mới nào trong sổ đăng ký §9). - Một chuỗi drand thứ hai được sử dụng trong sản xuất.
- Một phiên bản lớp bao bọc bên ngoài thứ hai.
Cho đến lúc đó §15 là một chỗ giữ chỗ cố định hình dạng di chuyển để các PR tương lai đáp xuống một mục tiêu đã biết thay vì tái thẩm bề mặt thương lượng từ đầu.
16. Nhật ký minh bạch và các cấp độ bền vững (Thiết kế — đã hoàn tất rà soát)
Trạng thái. Phần này là một đặc tả thiết kế. Các định dạng wire, băm, và mô hình tin cậy bên dưới là quy phạm đối với việc triển khai, nhưng chưa có mã nguồn nhật ký minh bạch nào được phát hành. Cuộc rà soát bên ngoài W5 đã hoàn tất: §16.15 ghi lại các quyết định đã được giải quyết và các ràng buộc ra mắt có tính bắt buộc phát sinh từ đó. Việc triển khai có thể tiến hành theo các ràng buộc đó. §16 vẫn mang tính hướng tới tương lai theo cùng nghĩa như §15 — nó cố định mục tiêu để việc triển khai đáp xuống một thiết kế đã được giải quyết thay vì tái thẩm mô hình tin cậy trong quá trình rà soát mã. Nó hoàn toàn mang tính bổ sung — mọi qub hiện có vẫn giữ giao dịch Arweave riêng của nó và không có thay đổi nào đối với định dạng wire
SealedQub/QubEnvelope.
16.1 Lý do căn bản và các cấp độ bền vững
Ngày nay, cả tính bền vững của một qub lẫn cam kết thời gian của nó đều dựa trên một giao dịch Arweave duy nhất cho mỗi qub (§11). Điều đó gắn độ trễ niêm phong với tính chung cuộc của Arweave, biến việc tải lên cho mỗi qub thành một trần chi phí sản phẩm (ARWEAVE_DAILY_CEILING), và không cung cấp thứ tự chống can thiệp nào giữa các qub. Nhật ký minh bạch thêm hai lớp bên dưới và xung quanh cấp duy nhất đó:
| Cấp | Tên | Đảm bảo | Khi nào |
|---|---|---|---|
| T1 | Xác nhận đồng bộ R2-trước | Sàn bền vững — các byte đã niêm phong được ghi vào nơi lưu trữ bền vững trước khi việc niêm phong trả về (< 300 ms p95). |
Mọi qub, đồng bộ (§16.10). |
| T2 | Đưa vào nhật ký minh bạch theo lô | Cam kết chỉ-thêm phổ quát, chống can thiệp + thứ tự toàn phần, neo vào Arweave. | Mọi qub, trì hoãn + theo lô (§16.5–16.7). |
| T3 | Tính vĩnh viễn Arweave cho mỗi qub | Một giao dịch Arweave riêng cho qub. | Bán thêm trả phí, và phương án dự phòng khi Arweave không khả dụng (§16.8). |
T2 biến Arweave cho mỗi qub thành một lựa chọn (T3) thay vì con đường bền vững duy nhất. ARWEAVE_DAILY_CEILING được loại bỏ với vai trò một trần sản phẩm và bị hạ cấp xuống làm một ngắt mạch chỉ trên ví neo chuyên dụng (§16.7); việc niêm phong của người dùng không bao giờ bị từ chối vì vượt quá trần đó.
Tính trung thực về bền vững (đã giải quyết — §16.15 Q6). Tính bền vững không thoái lui: việc ghi T1 vào R2 là đồng bộ và ghi-một-lần, nên một qub cấp miễn phí không mua T3 vẫn hoàn toàn bền vững ngay khoảnh khắc việc niêm phong trả về. Cái bị thô hóa là thời điểm cam kết cận trên có thể chứng minh được: đối với một qub miễn phí, nó trở thành thời gian khối neo thay vì thời gian khối của một giao dịch riêng cho mỗi qub. Ở khối lượng thấp — trạng thái ra-mắt-sớm và ngoài-giờ-cao-điểm thực tế — nhịp đầy đủ hằng ngày là sàn điển hình, không phải một trường hợp biên hiếm gặp. Do đó cách định khung sản phẩm là một cận trên với không có độ trễ số cam kết — "đã niêm phong và bền vững ngay bây giờ; một dấu thời gian công khai độc lập được thêm vào tại lần neo nhật ký kế tiếp (thường là hằng ngày)" — và bằng chứng cam kết theo giờ chính xác là một thuộc tính T3 trả phí, được tiết lộ tại bề mặt so sánh các cấp và trong điều khoản (§16.11, §16.15 Q6). Bất kỳ giới hạn thời gian nào cũng chỉ là một SLO nội bộ, không bao giờ là một SLA được tiếp thị.
16.2 Cấu trúc LogLeaf (hai hình dạng đã cam kết)
Một mục nhật ký là một LogLeaf, được mã hóa dưới dạng CBOR chuẩn viết tay theo hồ sơ §3.1 (độ dài xác định, không tag, không số thực, số nguyên dạng ngắn nhất, văn bản NFC, các trường tùy chọn bị loại bỏ khi vắng mặt, các khóa được sắp xếp theo độ dài byte mã hóa tăng dần rồi theo byte). Lá chắn chuẩn phân tích → tái mã hóa → so sánh của §3.1 được áp dụng trên đường mã hóa trước khi băm (không chỉ khi giải mã), nên hai triển khai không thể bất đồng về các byte của lá thông qua một khác biệt về độ rộng số nguyên hay thứ tự khóa. Tất cả số nguyên là u8 / u64 / i64; tất cả bản tóm tắt là chuỗi byte 32 byte (bstr[32]). Một id giao dịch Arweave được lưu trữ là một bản tóm tắt SHA-256 thô 32 byte được mang dưới dạng bstr[32], không bao giờ là một chuỗi văn bản base64url (khớp với §3.3).
Lá có hai hình dạng được chọn bởi một byte kind, bởi vì trên đường tải lên mặc định, Worker mù byte: POST /api/v1/upload chỉ nhận qub_id và unlock_at dưới dạng các khẳng định không tin cậy của client — body_hash, drand_round, created_at, và drand_chain_version đều được niêm phong bên trong lớp bao bọc bên ngoài §13, mà Worker không bao giờ giữ khóa của nó. Chỉ đường niêm phong phía máy chủ (POST /api/v1/seal) suy dẫn body_hash / drand_round từ bản rõ. Do đó một hình dạng lá duy nhất mang body_hash + drand_round sẽ cam kết những giá trị mà người vận hành chưa bao giờ xác minh đối với đa số qub thực tế. Việc tách giữ cho mọi giá trị đã cam kết là trung thực:
| Khóa | Độ dài mã hóa | Loại | Sự hiện diện | Ý nghĩa |
|---|---|---|---|---|
seq |
4 | u64 |
bắt buộc | Chỉ số lá toàn cục bắt-đầu-từ-0; vị trí mà bằng chứng đưa-vào cam kết tới. |
kind |
5 | u8 |
bắt buộc | 0x01 đã chứng thực (niêm phong phía máy chủ) hoặc 0x02 đã khẳng định (niêm phong phía client / tải lên mù byte). |
ref |
4 | bstr[32] |
bắt buộc | Id tham chiếu lá. Đã chứng thực → qub_id thô. Đã khẳng định → id đã làm mù SHA3-256(qub_id ‖ log_blind_secret) (§16.2.1). |
chash |
6 | bstr[32] |
bắt buộc | Địa chỉ nội dung SHA3-256(stored_bytes) — mối ràng buộc nội dung duy nhất mà Worker luôn có thể tính toán một cách trung thực, trên cả hai đường. |
unlock_at |
10 | i64 |
bắt buộc | Được sao chép (đã chứng thực) hoặc đã khẳng định (đã khẳng định); được xác thực > 0 trước khi nó vào lá. |
received_at |
12 | i64 |
bắt buộc | Đồng hồ tường của Worker tại thời điểm xác nhận R2. Không có giá trị bằng chứng (do người vận hành khẳng định; §16.6). Hiện diện để tự mô tả, không bao giờ là một bằng chứng. Được xác thực > 0. |
body_hash |
10 | bstr[32] |
chỉ kind=0x01 |
Bị loại bỏ trên 0x02 — Worker không có nó theo §13. |
drand_round |
12 | u64 |
chỉ kind=0x01 |
Bị loại bỏ trên 0x02. |
Một lá kind=0x02 cố ý không cam kết cả body_hash lẫn drand_round: nó chứng thực cam kết và thứ tự của một ciphertext mờ tại địa-chỉ-nội-dung chash, tuyên bố qub_id và unlock_at — không phải bản rõ hay vòng của nó. Các chân bản-rõ/vòng cho một qub đã khẳng định đến từ việc xác minh gói .qub §11 hiện có, không phải từ nhật ký (§16.11). drand_chain_version không nằm trong lá (nó nằm bên trong lớp bao bọc trên đường mặc định); độ chi tiết chuỗi nằm trên cái neo (§16.7). Kỷ luật mã hóa: từ chối một ref hoặc chash toàn-số-0, và từ chối unlock_at / received_at không-dương, phản chiếu lá chắn trị canh gác outcome_at > 0 trong cbor.rs.
16.2.1 Làm mù qub riêng tư
Nhật ký không được trở thành cái oracle liệt kê mà lớp bao bọc bên ngoài §13 tồn tại để ngăn chặn (§13.1). Đối với một qub riêng tư (đã bao bọc), lá asserted cam kết định danh đã làm mù SHA3-256(qub_id ‖ log_blind_secret), trong đó log_blind_secret là một bí mật do máy chủ giữ, và loại bỏ body_hash. Một bên thứ ba không thể buộc một lá như vậy với một qub_id cụ thể; người giữ qub, người có liên kết bàn giao và do đó có qub_id, có thể tính lại phép làm mù để xác nhận việc đưa-vào của chính mình. Một qub công khai (vốn đã có thể liệt kê, vốn đã mang tag Arweave Visibility: public theo §13.8) cam kết qub_id thô. Đây là một chỗ duy nhất mà khả năng xác minh độc lập cố ý nhường cho một bất biến riêng tư chịu tải; mối ràng buộc độc lập cho các qub riêng tư là chash (§16.9).
Quyền giám hộ log_blind_secret (đã giải quyết — §16.15 Q4). Phép làm mù bảo vệ tính không-thể-liên-kết của lá, không phải tính bảo mật của bản rõ (lớp bao bọc §13 nắm giữ điều đó một cách độc lập). Khi log_blind_secret bị lộ, đối với bất kỳ qub_id nào mà kẻ tấn công đã giữ hoặc có thể tái dựng (mọi qub mà nó có gói/URL, cộng với bất kỳ qub_id có entropy thấp hoặc công khai nào), nó tính lại ref của lá trong một phép băm và liên kết nó — đây là sự liên kết trực tiếp của một quần thể đã biết, không phải một phép vét cạn trên một không gian chưa biết. Phân loại log_blind_secret là một bí mật cấp tương-quan/Sybil thuộc cùng cấp giám hộ với các bí mật máy chủ khác, và chỉ luân chuyển tiến (một lần luân chuyển làm-mù-lại các lá tương lai; nó không thể hồi tố hủy-liên-kết các lá đã được neo).
16.3 Băm lá và băm nút
Băm phân tách miền theo RFC 6962 §2.1 với SHA-256 được thay bằng SHA3-256:
leaf_hash = SHA3-256(0x00 || canonical_cbor(LogLeaf))
node_hash(l,r) = SHA3-256(0x01 || l || r)
empty tree = SHA3-256("") // defined but never anchored
Các byte tiền tố miền 0x02 (chuỗi mục, §16.4) và 0x03 (băm STH, §16.6) được dành riêng và rời rạc với các byte này. Chúng là các byte đơn nên không thể trùng với các dấu phân cách miền ASCII 10 byte hiện có (QUB_ID_V2, v.v.). Cây là cây RFC 6962 mất-cân-bằng đầy-bên-trái (mỗi điểm chia bên trong tại lũy thừa lớn nhất của hai nhỏ hơn hẳn số lá của cây con), điều này cho phép các bằng chứng đưa-vào và bằng chứng nhất quán chia sẻ một thuật toán đường-kiểm-toán. Đặc tả tham chiếu mang theo mã giả suy dẫn trái/phải tường minh và cố định một vector kiểm tra không-lũy-thừa-của-hai (5 lá) để trường hợp thăng cấp cạnh-phải — mà một vector 4-lá che giấu — được vận dụng.
16.4 Móc xích băm (nội bộ)
LogDO duy trì một chuỗi mục nội bộ chỉ để nhất quán khi sự cố. Nó không bao giờ được công bố và không bao giờ hướng tới trình xác minh:
entry_chain[seq] = SHA3-256(0x02 || entry_chain[seq-1] || leaf_hash[seq])
entry_chain[-1] = SHA3-256("QUB_TLOG_GENESIS_V1")
Thẩm quyền chỉ-thêm được công bố là gốc Merkle tích lũy + cái neo của nó (§16.5–16.6), không bao giờ là thứ tự thô mà người vận hành tình cờ phục vụ các lá: chuỗi tính lại cho bất kỳ thứ tự nào được phục vụ, nên chỉ gốc đã được neo mới ghim vị trí chuẩn.
16.5 Cây Merkle tích lũy và việc gom lô
Có một cây RFC 6962 luôn-lớn-dần trên tất cả các lá theo thứ tự seq — không phải các cây riêng-lẻ theo từng lô. (Một cấu trúc móc-xích-lá-mang-sang theo từng lô đã bị bác bỏ: nó không phải một quan hệ tiền tố thực sự, nên "các bằng chứng nhất quán" của nó là không vững.) Cây tích lũy cho các bằng chứng nhất quán RFC 9162 đích thực và cho phép một cái neo gần đây duy nhất chứng minh việc đưa-vào cho bất kỳ qub cũ hơn nào.
Durable Object LogDO là người ghi duy nhất (blockConcurrencyWhile, phản chiếu QuotaDO / EntitlementDO) — việc thêm vào một nhật ký dùng chung là đọc-sửa-ghi trên trạng thái dùng chung và do đó PHẢI đi qua một DO, không bao giờ qua KV. Nó cache biên giới cạnh-phải của cây (O(log n) phép băm) nên việc đóng một lô là O(batch). Một lô là tập hợp các lá được neo cùng nhau; các điều kiện kích hoạt của nó là cấu hình được, không bị giao thức cố định: một bước tiến tree_size ít nhất là LOG_BATCH_MAX_LEAVES (mặc định 4096), hoặc tuổi đạt tới nhịp neo, hoặc một lần xả cưỡng bức khi một lần niêm phong T3 trả phí đáp xuống. root_i là Băm Cây Merkle tích lũy trên các lá 0 .. tree_size_i.
16.6 Đầu cây đã ký thông qua cái neo Arweave
Giao dịch neo Arweave chính là Đầu Cây Đã Ký và thay thế một chữ ký người vận hành cho chính đầu cây: cái neo hằng ngày không cần khóa qub nào vì owner của giao dịch Arweave chính là chữ ký. Luận điểm hào nước đứng vững — nền tảng bất biến, không phải một bí mật do qub giữ, mới là cái chịu tải cho gốc đã được neo.
Có chính xác một khóa ký qub ấm trong thiết kế, và nó được ghim: khóa biên nhận cho mỗi-lần-niêm-phong (§16.10). Khóa công khai của nó được cam kết trong LogProfile (phân phối cùng trình xác minh) và được đối-ký bởi anchor_owner, nên một trình xác minh xác thực một biên nhận đối với cùng một gốc đã được ghim như cái neo. Đây là cách giải quyết §16.15 Q2 — một khóa biên nhận không-ghim, có-thể-luân-chuyển-bởi-người-vận-hành sẽ có thể bị thoái thác (người vận hành có thể phủ nhận khóa là của họ), điều này sẽ làm vô hiệu giá trị quy trách của biên nhận đối với kẻ thù cấp-người-vận-hành mà biên nhận tồn tại để ngăn cản. Vậy: qub không giữ khóa ký nhật ký không-ghim nào; khóa biên nhận được ghim và được anchor_owner đối-ký.
SignedTreeHead là CBOR chuẩn (các khóa theo độ dài mã hóa): size:u64, root:bstr[32], batch:u64, prev:bstr[32] (sth_hash trước; khởi nguồn = 32 byte zero), log_id:bstr[32], first_seq:u64, anchored_at:i64. Băm của nó là sth_hash = SHA3-256(0x03 || canonical_cbor(SignedTreeHead)).
Gốc tin cậy được ghim. log_id = SHA3-256("QUB_TLOG_V1" || anchor_owner_address). Một trình xác minh tuân thủ PHẢI yêu cầu anchor_tx.owner == LogProfile.anchor_owner, trong đó anchor_owner (và khóa công khai của khóa biên nhận) được nung vào qub_core dưới dạng LogProfile — bên cạnh các hằng số quicknet đã có trong DrandTimelockProvider::quicknet() — và phân phối cùng tệp nhị phân trình xác minh. Trình xác minh cũng PHẢI xác minh ràng buộc dữ liệu → tx_id của giao dịch Arweave cục bộ thay vì tin một phản hồi /raw/ của cổng. Điều này đóng lỗ hổng nói-nước-đôi do ví giả mạo: "đã được neo trên Arweave" là vô nghĩa cho đến khi trình xác minh ghim ví nào.
Luân chuyển là một mở rộng quản trị §15, không phải một sự tái sử dụng (đã giải quyết — §16.15 Q3). Bề mặt hồ sơ của §15.2 hiện chỉ liệt kê các sig_alg / chuỗi drand / phiên bản bao bọc / loại nội dung, và danh sách điều kiện kích hoạt của §15.3 không có cái nào trong số này — LogProfile / anchor_owner chưa nằm trong bề mặt của §15. Do đó quản trị luân chuyển phải được xây dựng: §15.3 được mở rộng (bên dưới) để thêm điều kiện kích hoạt LogProfile, và một lần luân chuyển là một bản nâng LogProfile đã ký được phát hành trong một bản cập nhật trình xác minh. Một lần luân chuyển có kế hoạch mang theo một đối-ký đi-ra → đi-vào; một lần luân chuyển do bị lộ thì không thể (khóa đi-ra không tin cậy/không khả dụng đúng vào lúc đó) và lùi về bản nâng do-§15-quản-trị, với kiểm tra phân nhánh neo-trước (bên dưới) giới hạn thiệt hại trong khoảng tạm thời.
Cửa sổ nói-nước-đôi (tham số tin cậy hạng nhất). Một lá chỉ chống-nói-nước-đôi một khi cái neo bao phủ nó được Arweave xác nhận. Cửa sổ là received_at → xác nhận neo (≤ nhịp + tính chung cuộc của Arweave). Bên trong nó, các đảm bảo duy nhất là biên nhận niêm phong được ghim (§16.10) và tính toàn vẹn vận hành của qub. Ba tạo phẩm quy trách làm cho điều này trung thực thay vì khoa-tay-múa-chân (mô hình nhân chứng là cách giải quyết §16.15 Q2):
- Biên nhận niêm phong đã ký được ghim — bản tương tự SCT được trả về trong phản hồi tải lên (§16.10), được ký bởi khóa biên nhận được ghim, được
anchor_ownerđối-ký. Một lá bị bỏ rơi trước cái neo của nó để lại cho nạn nhân một biên nhận không-thể-thoái-thác để công bố, đóng lỗ hổng bỏ-sót-âm-thầm. - Phương pháp luận giám sát được công bố + đi-bộ-chuỗi-trước — chuỗi
prevcủa neo được đi-bộ từ đầu→khởi-nguồn; một phân nhánh (hai cái neo tại cùng mộtsizevớirootkhác nhau, hoặc mộtprevbị gãy) là bằng chứng có thể công bố về hành vi sai trái. Phát hiện nói-nước-đôi là một cam kết vận hành được tuyên bố, không phải một giả định âm thầm. - Các đầu cây tự-công-bố kép — mỗi đầu cây mới
{sth_hash, tree_size}được đăng lên một kho lưu trữ GitHub công khai, chỉ-thêm, do qub sở hữu chuyên dụng (chân tự-công-bố chống-can-thiệp chịu tải), với một bài đăng mạng xã hội chỉ làm chứng-thực nỗ-lực-tốt-nhất. Một lần đăng thất bại PHẢI báo động (không được thất bại âm thầm).
Giới hạn trung thực (ràng buộc bắt buộc). Vì qub kiểm soát cả hai bề mặt đăng, đây là tự-công-bố, không phải được làm-chứng độc lập. Không bề mặt sản phẩm, tiếp thị, hay pháp lý nào được tuyên bố rằng nhật ký được "làm chứng độc lập"; tuyên bố được phép là nói-nước-đôi là có thể phát hiện và để lại một biên nhận không-thể-thoái-thác. Một nhân chứng bên thứ ba độc lập thực sự được hoãn lại cho một bản nâng quản trị §15 tương lai.
received_at do người vận hành khẳng định và không tuyên bố nào được dựa vào nó — nó không bao giờ được trưng ra như bằng chứng hay như chứng-thực tranh chấp trên bất kỳ bề mặt sản phẩm / pháp lý / API / hiển-thị-bằng-chứng nào. Thời gian khối neo Arweave T là dấu thời gian không-cần-tin-cậy duy nhất (một cận trên trên "đã được ghi nhật ký bởi"). Bất kỳ kiểm tra-tỉnh-táo giám sát nào trên received_at PHẢI so sánh đối với T, không phải đối với trường STH anchored_at do người vận hành kiểm soát; một kiểm tra như vậy chỉ là một hàng rào chống một lỗi đồng hồ của người vận hành trung thực, không phải một kiểm soát quy trách đối với một người vận hành ác ý (§16.15 Q5).
16.7 Định dạng giao dịch neo và nhịp
AnchorBundle là thân giao dịch Arweave dạng CBOR-chuẩn, được ghi qua bộ-gom-bó §16.8: ver:u8, sth:bstr (các byte SignedTreeHead chuẩn), prev_anchor:bstr (id giao dịch neo trước dạng byte thô; bị loại bỏ tại khởi nguồn), chain_hash:tstr (chuỗi drand đang hiệu lực — quicknet), và luồng lá-CBOR của lô theo thứ tự seq để cái neo tự-chứa: một bộ giám sát tái-suy-dẫn root từ thân với phụ thuộc vào qub bằng không. (Nếu luồng lá trở nên lớn ở khối lượng cao, một bản sửa đổi tương lai có thể chỉ cam kết một dải lá bằng tham chiếu; được ghi nhận, không được áp dụng trong v1.)
Các tag Arweave cố ý có thể liệt kê — nhật ký được dành để được tìm thấy, khác với các qub riêng tư: App-Name: qub-tlog, Anchor-Format: 1, Log-Id: <hex>, Batch: <n>, Tree-Size: <n>, Root: <hex>, Prev-Anchor: <tx>, Content-Type: application/cbor. Các tag là các gợi ý không tin cậy; thân CBOR là thẩm quyền duy nhất.
Nhịp: mặc định hằng ngày, được xem xét lại theo khối lượng (điều kiện kích hoạt theo kích thước tự động rút ngắn nhịp hiệu dụng dưới tải); một lần niêm phong T3 trả phí cưỡng bức một cái neo nên các khách hàng trả phí không bao giờ phải chờ một ngày. Ví neo là chuyên dụng và vận-tốc-thấp, tách biệt với ví tải lên — nó PHẢI là JWK riêng của nó (một khóa riêng biệt, không phải một vai trò logic trên ví tải lên) để một sự cố lộ ví-tải-lên không thể giả mạo các cái neo — với một ngân sách cứng giao-dịch-neo cho mỗi ngày (ARWEAVE_DAILY_CEILING bị hạ cấp). Tư thế giám hộ được tuyên bố thẳng thắn: một khóa nóng phạm-vi-hẹp với một ngắt mạch chặt và số dư thấp, không phải "lạnh" — một ví tự-ký hằng ngày không thể là lạnh, và đặc tả không giả vờ khác đi.
16.8 Bộ gom bó ANS-104
Một bộ mã hóa DataItem ANS-104 và bộ ký deep-hash tự xây, khoảng 300 dòng, chỉ Web Crypto, không phụ thuộc npm nào (cả hai Turbo SDK đều trượt cổng chuỗi-cung-ứng npm ci --ignore-scripts). Bố cục byte của DataItem:
signatureType (2, LE) || raw_signature || owner || target(flag+0|32) || anchor(flag+0|32) || num_tags(8, LE) || tags_len(8, LE) || avro_tags || data
Việc ký là deepHash của Arweave — một bản tóm tắt SHA-384 đệ quy (yêu cầu wire của Arweave, crypto.subtle.digest("SHA-384")) trên ["dataitem", "1", sig_type, owner, target, anchor, encoded_tags, data] — rồi RSA-PSS trên deep hash với JWK của ví thông qua crypto.subtle; id = base64url(SHA-256(signature)). SHA-384 ở đây được cách ly như một nguyên thủy chỉ-dành-cho-wire-Arweave, không bao giờ là một nguyên thủy tin cậy của qub (§15 ghi lại hàng rào; băm tin cậy của qub là SHA3-256 xuyên suốt).
Một đường mã phục vụ ba bên tiêu thụ: tính vĩnh viễn T3 cho mỗi-qub trả phí, phương án dự phòng khi Arweave không khả dụng (xếp hàng DataItem, trả về xác nhận R2-trước bất kể thế nào — điều này đóng ngõ-cụt 503 ARWEAVE_UNAVAILABLE hiện tại), và việc ghi AnchorBundle. Sơ đồ chữ ký (đã giải quyết — §16.15 Q8): v1 ký bằng RSA-PSS (loại chữ ký 1) tái sử dụng cơ chế JWK ví Arweave hiện có (không có quyền giám hộ khóa tồn-tại-lâu mới nào, phục vụ luận điểm "một bí mật ít hơn"); Ed25519 được hoãn lại cho đường di-cư PQ của §15.
Deep hash tự-cuộn là mã rủi-ro-cao-nhất, độ-phủ-tự-nhiên-thấp-nhất trong W5, nên việc kiểm soát nó là không thể thương lượng (§16.15 Q8):
- Vector kiểm tra đa-ngôn-ngữ
tlog_v1.json(Rust + TS, mẫuwrapper_v1.jsoncủa §14.5) bao phủ deep-hash, các byte DataItem + id, các băm lá, một gốc 5-lá + đường kiểm toán, một băm STH, một bằng chứng đưa-vào, và một bằng chứng nhất quán — theo cả hai chiều ký và xác minh (chiều xác minh quan trọng vì kiểm tra cục bộ tx → tx_id của §16.6 kéo deep hash vào mọi trình xác minh độc lập, không chỉ người ghi). - Một lần kiểm tra liên-thông quay-vòng một lần qua một bộ-gom-bó ANS-104 tham chiếu, được tiêu thụ dưới dạng chỉ dữ liệu kiểm tra tĩnh — không bao giờ là một phụ thuộc thời-gian-chạy npm (tư thế chỉ-Web-Crypto / không-script-cài-đặt đứng vững).
- Đường deep-hash + RSA-PSS phải quay-vòng qua cùng các nguyên thủy
crypto.subtlemà production dùng, để bộ mã hóa tự xây tương thích byte. - Một bộ giám sát chấp nhận sau-gom-bó liên tục xác nhận mỗi DataItem neo / dự phòng thực sự đạt được sự chấp nhận của Arweave, với một báo động + ngắt mạch — vì deep hash cũng phục vụ hàng đợi dự phòng khi Arweave không khả dụng, nên một thoái lui âm thầm sẽ lấp đầy hàng đợi đó bằng các mục bị-mạng-từ-chối đúng vào lúc gián đoạn mà nó tồn tại để bao phủ.
16.9 Bằng chứng đưa-vào và bằng chứng nhất quán
Cả hai đều theo RFC 9162, SHA3-256, được phục vụ dưới dạng CBOR chuẩn.
InclusionProof — GET /api/v1/qub/:tx_id/proof: ver:u8, leaf:bstr (CBOR lá chính xác — trình xác minh tự tính lại leaf_hash và không bao giờ tin một băm được cung cấp), index:u64, size:u64, audit:[bstr[32]], root:bstr[32], anchor:{ txid:bstr[32], batch:u64, sth:bstr[32], log_id:bstr[32], block_height?:u64, anchored_at?:i64 }.
ConsistencyProof — GET /api/v1/log/consistency?first=<size_a>&second=<size_b>: ver:u8, first_size:u64, second_size:u64, first_root:bstr[32], second_root:bstr[32], nodes:[bstr[32]], first_anchor, second_anchor. Một danh sách khóa rõ ràng duy nhất, được ghim bởi vector kiểm tra.
Xác minh độc lập (không có máy chủ qub, mở rộng §11):
1. Parse .qub bundle → SealedQub; recompute qub_id (§4.1).
2. Read leaf.kind.
3a. kind=0x01 (attested):
assert leaf.ref == qub_id
assert leaf.body_hash == SHA3-256(body)
assert leaf.drand_round == unlock_round(unlock_at)
3b. kind=0x02 (asserted):
assert leaf.ref == SHA3-256(qub_id || blind) // holder supplies blind
OR treat ref as opaque and bind via leaf.chash == SHA3-256(stored_bytes)
4. Recompute leaf_hash = SHA3-256(0x00 || leaf); fold `audit` per RFC 6962
using index/size; require derived root == proof.root.
5. Fetch anchor.txid from any gateway; verify the tx data → tx_id binding
(do not trust a gateway /raw/ response); REQUIRE anchor_tx.owner ==
LogProfile.anchor_owner.
6. Parse AnchorBundle; require committed root == proof.root and size ==
proof.size; read the Arweave block time T.
7. Emit the claim scoped by leaf.kind (§16.11).
Nơi lưu trữ phục vụ bằng chứng PHẢI được khóa theo tọa độ (đã giải quyết — §16.15 Q7, điều kiện tiên quyết chặn). Việc sinh bằng chứng cho lá-nguội là trung-lập-về-tính-đúng-đắn chỉ khi vật liệu kiểm toán R2 là một kho nút Merkle bền vững được khóa theo tọa độ cây tuyệt đối (level, index) — không phải các delta nút theo từng batch. Với một kho được khóa theo tọa độ, bất kỳ đường kiểm toán (leaf i, size N) nào cũng là một tập hợp O(log N) phép GET R2 trực tiếp với không tính-lại nào qua các ranh giới lô; với một kho được khóa theo lô thì không, đó chính là khoảng-trống-bố-trí-lưu-trữ mà cách giải quyết này đóng lại. Các thân lá cũng tương tự có thể địa-chỉ-hóa-theo-nội-dung bởi seq. Một vector kiểm tra W5 PHẢI chứng minh một lá nguội thời-khởi-nguồn đối với một gốc muộn-hơn-nhiều chỉ dùng R2 + Arweave với kho lưu trữ LogDO bị xóa sạch, để tuyên bố an-toàn-thu-hồi trong §16.13 được chống lưng thay vì khẳng định. Các phép GET R2 tuần tự O(log N) chỉ thuộc về riêng điểm cuối bằng chứng bất đồng bộ — không bao giờ trên đường nóng niêm phong (§16.10) hay một cron theo-mỗi-tick.
16.10 Thứ tự xác nhận R2-trước
Trình tự POST /api/v1/upload trở thành:
- Các hàng rào nửa-trước (xác thực, xác minh, khóa shard idempotency) — không thay đổi.
- Đồng bộ
await QUB_CACHE.put(qub-cache/<tx_id>, wrappedBytes)— sàn bền vững; cũng đóng cuộc đua precache của W1 (trước đây là mộtctx.waitUntilsau khi gửi Arweave). - Đồng bộ
await LogDO.append(leaf)— một RPC DO trong-colo; người ghi duy nhất gánseq, mở rộng chuỗi mục, và cập nhật biên giới. (Đọc-sửa-ghi trên trạng thái dùng chung → DO, không bao giờ KV.) RPCappendchỉ làm điều đó — công việc đóng-lô-MerkleO(batch)chạy ngoài RPC này trên báo động của LogDO, nếu không p95 củaappendsẽ vọt lên mỗi lần niêm phong thứLOG_BATCH_MAX_LEAVES. - Trả về xác nhận ngay bây giờ — kèm biên nhận niêm phong (được ký bởi khóa biên nhận được ghim, §16.6) và
{ tx_id, log_seq, anchor_status: "pending" }. Việc lan-tỏa Arweave nhiều-giây được loại khỏi đường tới hạn. - Một
ctx.waitUntilxếp hàng công việc trì hoãn: việc gửi Arweave cho mỗi-qub (giờ là nỗ-lực-tốt-nhất / trả phí; khi thất bại nó định tuyến tới hàng đợi dự phòng của bộ gom bó thay vì trả 503 cho người dùng) cộng với các lần ghi meta tạm thời hiện có. Việc đóng lô và neo chạy độc lập từ báo động của LogDO và cron neo hằng ngày. Không cóctx.waitUntilbên trong một vòng lặp; khóa shard idempotency hiện có được bảo toàn.
Ngân sách độ trễ (đã giải quyết — §16.15 Q7). Mục tiêu p95 < 300 ms là một cổng ra mắt được đo lường, không phải một giả định. Đường tới hạn trung thực là các lần đọc KV nửa-trước + một phép PUT R2 + hai Durable Object tuần tự — lần ghi nợ hạn-ngạch-niêm-phong QuotaDO hiện có và lần append LogDO mới — nên ngân sách phải tính tới hai vòng-khứ-hồi DO trong-colo, không phải một. Phát hành một báo động độ trễ LogDO phản chiếu của QuotaDO và xem một thoái lui p95 là một vật cản phát hành.
16.11 Mô hình tin cậy — tuyên bố chính xác, được phạm-vi-hóa theo loại lá
Đối với kind=0x01 (đã chứng thực): "Nội dung này — thân khớp với body_hash, được định danh bởi qub_id — đã được cam kết vào nhật ký chỉ-thêm của qub tại vị trí seq và đã tồn tại không muộn hơn thời gian khối Arweave T; nó không thể đọc được về mặt mật mã cho đến vòng drand R = unlock_round(unlock_at)." Đây là bộ ba {ràng buộc vòng tlock + đưa-vào Merkle + gốc đã neo} đầy đủ.
Đối với kind=0x02 (đã khẳng định, mặc định): "Một ciphertext mờ với địa-chỉ-nội-dung chash, tuyên bố qub_id và unlock_at, đã được cam kết vào nhật ký chỉ-thêm tại vị trí seq và đã tồn tại không muộn hơn thời gian khối Arweave T." Các chân vòng và thân được cung cấp bởi việc xác minh gói .qub §11 hiện có (qub_core::unlock), không phải bởi nhật ký; cái mà nhật ký thêm vào so với một giao dịch trần cho mỗi-qub là thứ tự chống-can-thiệp, một thời điểm cam kết cận-trên không-cần-tin-cậy, và khả năng chống nói-nước-đôi.
Cả hai tuyên bố đều loại trừ, theo §11: quyền tác giả mà không có sig_alg ≥ 0x01, ý định, và định thời ở độ chi tiết dưới-cấp-neo. Không tuyên bố nào được dựa vào received_at.
Trần tuyên bố (ràng buộc ra mắt bắt buộc — đã giải quyết §16.15 Q1). Đối với các qub miễn phí / mặc định (kind=0x02), tuyên bố kind=0x02 được phạm-vi-hóa ở trên là trần của những gì bất kỳ bề mặt sản phẩm, tiếp thị, điều khoản, hay hiển-thị-bằng-chứng nào được khẳng định. Không bề mặt nào được tuyên bố hay ngụ ý rằng nhật ký chứng minh nội dung hoặc vòng mở khóa của một qub mặc định — nhật ký chứng minh thứ tự + một thời điểm cam kết cận-trên không-cần-tin-cậy của một ciphertext mờ. Bằng chứng nội dung và vòng đến độc quyền từ việc xác minh gói .qub §11 hiện có, vốn độc lập với nhật ký. Đây là một vật cản ra mắt cứng trên câu chữ, không phải một sở thích phong cách; nó là cách giải quyết giữ cho đường mặc định mù-byte được trung thực.
16.12 Phiên bản hóa và phối hợp W3
Không có lần nâng wire SealedQub và do đó không có lần nâng phiên bản giao thức (§12.1): nhật ký là một sidecar cam kết tới các trường và byte hiện có, nên nó không bước vào lịch sử phiên-bản-giao-thức của §12.2. drand_chain_version tùy chọn của W3 không bị động tới và vẫn là trường SealedQub tùy chọn duy nhất. Thay vào đó, nhật ký giới thiệu các không gian phiên bản độc lập của riêng nó — LOG_VERSION_1, ANCHOR_FORMAT_1, InclusionProof.ver — phản chiếu tính độc lập của phiên bản bao bọc §12.4 (lớp bao bọc mang một byte phiên bản độc lập với phiên bản giao thức, và các phiên bản nhật ký theo cùng sự tách biệt đó).
Việc phân phối bằng chứng được fetch theo mặc định, với một tùy chọn đi-kèm. Một bằng chứng không thể tồn tại tại thời điểm niêm phong (cái neo chưa được ghi), nên gói .qub tại thời điểm niêm phong vẫn không-có-bằng-chứng. Trình xác minh của W7 fetch GET …/proof một lần, hoặc ở chế độ hoàn-toàn-ngoại-tuyến tái dựng bằng chứng từ AnchorBundle công khai thông qua một truy vấn Arweave trên Log-Id. Gói .qub (W7) dành riêng một thành viên inclusion_proof tùy chọn — vắng mặt tại thời điểm niêm phong, được điền bởi một lần tái-xuất sau-neo cho lưu trữ nguội — theo cùng mẫu "tùy chọn, bị loại bỏ theo mặc định, bổ sung" như drand_chain_version của W3.
16.13 Lưu giữ
Các cửa sổ lưu giữ cho phần đuôi mở của LogDO, nền phục-vụ-bằng-chứng R2, các bộ đếm ngắt-mạch neo, và hàng đợi dự phòng của bộ gom bó được đặc tả trong docs/DATA-RETENTION.md. Nguyên tắc: nơi lưu trữ nóng theo-mỗi-mục của nhật ký (LogDO) có thể thu hồi sau-neo; vật liệu kiểm toán của nó — kho nút Merkle được khóa theo tọa độ (level, index) + các thân lá được địa-chỉ-hóa-theo-seq (§16.9) + các cái neo Arweave — là vĩnh viễn. Việc thu hồi một lá nguội khỏi DO không bao giờ làm vô hiệu một bằng chứng đã phát hành, vì một bằng chứng phân giải đối với kho nút R2 vĩnh viễn đó và cái neo Arweave, không phải DO (và vector kiểm tra DO-bị-xóa-sạch của §16.9 chứng minh điều đó).
16.14 Các vector kiểm tra
W5 phát hành vector kiểm tra đa-ngôn-ngữ tlog_v1.json (§16.8) cộng với các vector đã-tính-toán: một lá kind=0x01 và một lá kind=0x02 → leaf_hash; gốc tích lũy 5-lá; một bằng chứng đưa-vào; một bằng chứng nhất quán; một AnchorBundle; và một id DataItem. Chúng nằm cạnh các vector lớp-bao-bọc-bên-ngoài §14.5 và được vận dụng bởi cả hai triển khai Rust (qub-core) và TypeScript (Worker).
16.15 Các quyết định rà soát (W5 — đã giải quyết)
Cuộc rà soát bên ngoài W5 (một lượt thiết kế đối kháng + phê duyệt của chủ sở hữu) đã hoàn tất. Mỗi quyết định bên dưới đã được giải quyết và được phản ánh trong văn bản §16 ở trên; các ràng buộc ra mắt bắt buộc được nêu lại ở cuối. Việc triển khai có thể tiến hành theo chúng.
- Tính trung thực của lá đường-mặc-định (
kind=0x02) — ĐÃ GIẢI QUYẾT. Phát hành việc tách hai-loại-lá như đã đặc tả:kind=0x02không cam kết cảbody_hashlẫndrand_round. Không có trường*_body_hashnào trên đường mù byte (nó sẽ là tín hiệu "đã xác minh" sai dễ-đọc-nhất cho các nhà tích hợp và là một tiện ích mà §11 đã cung cấp từ gói). Đừng yêu cầu niêm phong phía máy chủ cho các qub được nhật ký chứng thực (điều đó sẽ ép bản rõ đi qua Worker và phá hủy hào nước crypto-shredding). Bất kỳ phép tắt-mạch tự-mô-tả nào đều thuộc về gói.qub/ phong bì bằng chứng dưới dạng một trường được-trình-xác-minh-tính-lại, không bao giờ là một trường lá. Trần tuyên bố được chủ-sở-hữu-xác-nhận: §16.11. - Quy trách nói-nước-đôi / bỏ-sót — ĐÃ GIẢI QUYẾT. Khóa biên-nhận-niêm-phong được ghim trong
LogProfile+ đượcanchor_ownerđối-ký (đóng mâu thuẫn "không có khóa ký" trước đó; §16.6). Mô hình nhân chứng ra mắt: biên nhận được ghim + phương pháp luận giám sát + đi-bộ-chuỗi-trước + các đầu cây tự-công-bố kép (kho GitHub công khai do qub sở hữu, mạng xã hội nỗ-lực-tốt-nhất), được tiếp thị là có-thể-phát-hiện + có-biên-nhận, không bao giờ được-làm-chứng-độc-lập. Một nhân chứng bên thứ ba thực sự được hoãn lại cho một bản nâng quản trị §15. - Gốc tin cậy anchor-owner được ghim + luân chuyển — ĐÃ GIẢI QUYẾT. Áp dụng phép ghim
LogProfile(§16.6); trình xác minh kiểm traanchor_tx.owner == anchor_ownervà xác minh ràng buộc dữ liệu → tx_id của giao dịch cục bộ. Quản trị luân chuyển là một mở rộng §15 cần xây dựng (thêm điều kiện kích hoạt §15.3), không phải một sự tái sử dụng; các lần luân chuyển có kế hoạch đối-ký, các lần luân chuyển do bị lộ lùi về bản nâng §15 với kiểm tra phân nhánh giới hạn thiệt hại. - Làm mù lá qub riêng tư — ĐÃ GIẢI QUYẾT. Giữ phép làm mù cho các qub riêng tư (
ref = SHA3-256(qub_id ‖ log_blind_secret)),qub_idthô cho các qub công khai (đã có ở §16.2.1),chashlà mối ràng buộc độc lập.log_blind_secretlà một bí mật cấp tương-quan/Sybil, chỉ luân chuyển tiến (§16.2.1). received_at— ĐÃ GIẢI QUYẾT. Giữ nó trong lá, đã cam kết nhưng tường minh không-có-giá-trị-bằng-chứng; không bao giờ được trưng ra như bằng chứng hay chứng-thực tranh chấp trên bất kỳ bề mặt nào. Bất kỳ kiểm tra-tỉnh-táo giám sát nào cũng so sánh đối với thời gian khối ArweaveT, không phải đối vớianchored_atdo người vận hành kiểm soát (§16.6).- Định thời có-thể-chứng-minh cấp miễn phí — ĐÃ GIẢI QUYẾT (phê duyệt của chủ sở hữu). Tính bền vững không thoái lui; chỉ thời điểm cam kết cận-trên có thể chứng minh bị thô hóa xuống thời gian khối neo. Câu chữ cấp miễn phí không dùng SLA số ("…được thêm vào tại lần neo nhật ký kế tiếp, thường là hằng ngày"); bằng chứng theo-giờ-chính-xác là một thuộc tính T3 trả phí, được tiết lộ tại bề mặt so sánh các cấp + trong điều khoản (§16.1).
- Cây tích lũy trên Workers — ĐÃ GIẢI QUYẾT. Một cây RFC 9162 tích lũy + LogDO người-ghi-duy-nhất có-cache-biên-giới (dư dả thoải mái so với trần ~1k ghi/giây của DO; hoãn việc phân-mảnh-gốc-Merkle-của-shard cho đến gần nó). Điều kiện tiên quyết chặn: kho nút R2 được khóa theo tọa độ
(level, index)+ vector kiểm tra lá-nguội DO-bị-xóa-sạch (§16.9);< 300 mslà một cổng ra mắt được đo lường trên hai DO tuần tự (§16.10). - Sơ đồ chữ ký ANS-104 + deep-hash — ĐÃ GIẢI QUYẾT. RSA-PSS (loại chữ ký 1, tái dùng JWK ví-neo chuyên dụng); Ed25519 hoãn lại cho đường PQ §15. Deep hash SHA-384 tự-cuộn được kiểm soát bởi vector kiểm tra liên-thông-triển-khai cả-hai-chiều, một kiểm tra liên-thông bộ-gom-bó-tham-chiếu chỉ-tĩnh, lần quay-vòng
crypto.subtle-dùng-chung, và bộ giám sát chấp-nhận-Arweave sau-gom-bó (§16.8).
Các ràng buộc ra mắt bắt buộc (mang vào triển khai + rà soát sản phẩm/pháp lý):
- Trần tuyên bố (Q1/Q6). Không bề mặt nào được nói nhật ký chứng minh nội dung hay vòng mở khóa của một qub mặc định; tuyên bố được phép là được sắp thứ tự, chống-can-thiệp, với một thời điểm cam kết cận-trên không-cần-tin-cậy. Câu chữ dấu thời gian cấp miễn phí không mang độ trễ số; bằng chứng theo-giờ-chính-xác chỉ là T3 trả phí.
- Tính trung thực về nhân chứng (Q2). Tiếp thị nói-nước-đôi là có-thể-phát-hiện + có-biên-nhận, không bao giờ được-làm-chứng-độc-lập.
- Khóa biên nhận + neo (Q2/Q8). Khóa biên nhận được ghim + đối-ký; ví neo là JWK riêng của nó tách biệt với ví tải lên.
- Cổng deep-hash (Q8). Không cái neo hay giao dịch T3 nào được phát hành cho đến khi vector kiểm tra cả-hai-chiều + kiểm tra liên-thông qua; bộ giám sát chấp nhận báo động khi thất bại.
- Điều kiện tiên quyết lưu trữ (Q7). Kho nút được khóa theo tọa độ + vector lá-nguội DO-bị-xóa-sạch là các điều kiện tiên quyết cho đảm bảo "việc thu hồi không bao giờ làm vô hiệu một bằng chứng".