Đặ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. |
| 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ư tuỳ 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ý
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
signature = Sign(author_secret_key, sig_input)
Dấu phân cách miền: "QUB_AUTHOR_SIG_V1" là 17 byte ASCII: [0x51, 0x55, 0x42, 0x5F, 0x41, 0x55, 0x54, 0x48, 0x4F, 0x52, 0x5F, 0x53, 0x49, 0x47, 0x5F, 0x56, 0x31]. Không có byte đệm.
Byte cuối: byte tiền ảnh thứ 91 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 cam kết với bốn trường của envelope: version, qub_id, body_hash, unlock_at (cộng với dấu phân cách miền cố định và byte org_id_present). Ba trong số bốn đó là các bất biến cấu trúc: 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 trực tiếp 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 |
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 |
sender_label |
✗ | Văn bản chỉ để hiển thị; có thể thay đổi mà không phá vỡ chữ ký |
reply_to |
✗ | Con trỏ luồng; có thể thay đổi mà không phá vỡ chữ ký |
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.) |
Hệ quả bảo mật của các trường không được xác thực.
- Một bên có quyền ghi vào các byte đã lưu trữ có thể đổi
sender_label("Alice" → "Mallory") mà không làm mất hiệu lực chữ ký tác giả.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. - Một trường
reply_tocũng có thể được chỉnh sửa sau khi ký. Vìqub_idđược địa chỉ hóa theo nội dung, kẻ tấn công không thể trỏreply_tođến một mục tiêu không tồn tại, nhưng họ có thể âm thầm gắn lại một trả lời với một qub hiện có khác.
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 (same formula as §9.3).
6. Verify(author_pubkey, sig_input, author_signature).
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.
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. - 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.
- Các phiên bản chính đã biết CÓ THỂ tha thứ các trường tùy chọn không xác định nếu các quy tắc tương thích thuận cho phép (các trường tùy chọn vắng mặt khỏi thứ tự khóa chuẩn sẽ bị bỏ qua).
- 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 tùy chọn không xác định (các khóa không trong thứ tự chuẩn §3.2) NÊN bỏ qua các khóa đó và tiến hành xác minh bằng các trường đã biết. Điều này cho phép các bổ sung nhỏ trong tương lai (ví dụ, siêu dữ liệu mới) mà không yêu cầu tăng phiên bản chính.
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). - Route niêm phong phía máy chủ Worker:
crypto.getRandomValues.
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.