ข้อกำหนดโปรโตคอล qub
qub เป็นโปรโตคอลสำหรับการผูกมัดเชิงเวลาแบบเข้ารหัส: ระบบสำหรับการผนึกคำไปยังวันที่ในอนาคตและต่อมาทำการตรวจสอบว่าอะไรที่ถูกผนึกไว้แน่นอน ซึ่ง drand จะกำหนดรอบในการเปิดเผย และ—เมื่อมีธุรกรรมการจัดเก็บหรือหลักฐานจากบันทึกความโปร่งใส—เป็นขอบเขตบนที่มีเวลาประทับอิสระเมื่อข้อความเข้ารหัสถูกผูกมัด
สามสิ่งพื้นฐานทำให้มันทำงานได้ drand เป็นสัญญาณสุ่มแบบกระจายศูนย์—วันที่เปิดเผยถูกบังคับโดยวิธีเข้ารหัส ไม่ใช่โดยความเต็มใจของ qub ที่เก็บข้อมูลทนทาน การเก็บรักษาบิตที่ถูกยอมรับและปิดผนึกไว้ในขณะที่เส้นทางการเผยแพร่ปัจจุบันกำหนดเวลาธุรกรรมการจัดเก็บถาวรแต่ละรายการ; การแนบบันทึกการอัปโหลดทั่วไปที่สำเร็จสามารถเข้าร่วมกับความมุ่งมั่นที่จัดกลุ่มและยึดแน่นเพิ่มเติมได้ ML-DSA-65 เป็นลายเซ็นต์ดิจิทัลหลังควอนตัม—เมื่อเปิดการระบุผู้เขียน qub จะถูกผูกกับคู่กุญแจที่ความลับไม่เคยออกจากอุปกรณ์ของผู้เขียน
สิ่งพื้นฐานเหล่านี้รวมกันสร้างข้อความที่ถูกล็อกเวลาและตรวจจับการปลอมแปลงได้ เป็นได้ทั้งที่สามารถระบุได้ และสามารถประทับเวลาทีละอิสระ—เป็นใบเสร็จที่คุณค่าของมันเพิ่มขึ้นตามความสามารถของโลกในการปลอมแปลงอดีต
ส่วนที่เหลือของเอกสารนี้คือข้อกำหนดมาตรฐานที่จำเป็นสำหรับการใช้งานร่วมกันได้
ข้อกำหนดโปรโตคอล qub
| สนาม | ค่า |
|---|---|
| การปล่อยเอกสาร | 1.0.0 (protocol-v1.0.0) |
| โพรโตคอลแบบสาย | 0x01 |
| ห่อชั้นนอก | 0x01 |
| วันที่มีผลบังคับใช้ | 23-09-2026 |
| สถานะ | ปัจจุบัน |
| ได้รับการตรวจทานแล้ว | 23-09-2026 |
เอกสารนี้เป็นข้อกำหนดโปรโตคอลเชิงบรรทัดฐานสำหรับระบบข้อผูกพันเชิงเวลา qub กำหนดโครงสร้างข้อมูล กฎการเรียงลำดับบิต สูตรการได้มาซึ่งค่า และขั้นตอนการตรวจสอบที่จำเป็นสำหรับการใช้งานที่ทำงานร่วมกันได้
ขอบเขต: ชั้นโปรโตคอลถูกออกแบบให้เป็นกลางทางภาษาโดยเจตนา — เนื้อหา qub เป็นข้อความ / มาร์กดาวน์ / ไบต์สัญญาที่ทึบแสง และการเรนเดอร์ตามภาษาท้องถิ่นเป็นความรับผิดชอบของผู้ชม (เว็บแอป qub.social, iframe <qub-embed>, ไคลเอนต์ MCP เป็นต้น)
1. สัญลักษณ์และข้อตกลง
| สัญลักษณ์ | ความหมาย |
|---|---|
u8, u64, i64 |
จำนวนเต็มไม่มีเครื่องหมาย/มีเครื่องหมายตามความกว้างบิตที่ระบุ |
[u8; N] |
อาร์เรย์ไบต์ความยาวคงที่ N ไบต์ |
Vec<u8> |
อาร์เรย์ไบต์ความยาวแปรผัน |
Option<T> |
ค่าประเภท T หรือไม่มีอยู่ |
String |
สตริงข้อความ UTF-8 ที่ผ่านการนอร์มัลไลซ์แบบ NFC |
| ` | |
SHA3-256(x) |
แฮช NIST SHA3-256 ของสตริงไบต์ x (FIPS 202) |
ceil(x) |
ฟังก์ชันปัดขึ้น: จำนวนเต็มที่น้อยที่สุดที่ ≥ x |
| CBOR | Concise Binary Object Representation (RFC 8949) |
| big-endian | ไบต์ที่มีนัยสำคัญที่สุดมาก่อน |
จำนวนเต็มทั้งหมดในการสร้างพรีอิมเมจถูกเข้ารหัสเป็นอาร์เรย์ไบต์ความกว้างคงที่ big-endian (i64 → 8 ไบต์, u8 → 1 ไบต์) ยกเว้นที่ระบุไว้เป็นอย่างอื่น
ไทม์สแตมป์ทั้งหมดเป็น วินาที Unix ใน UTC
2. โครงสร้างข้อมูล
2.1 ComposeQub (สถานะในหน่วยความจำของผู้สร้าง)
ไม่ได้เรียงลำดับบิตเป็น CBOR ไม่ได้เขียนลงพื้นที่เก็บถาวร อยู่เฉพาะในแอปผู้สร้าง
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, // 0x00 = private; 0x01 = public
content_type: u8, // 0x01 text; 0x03 pact; 0x04 verdict
plaintext: Vec<u8>, // Raw body bytes (UTF-8 for text)
sender_label: Option<String>, // Display name; V2-signed when authorship is enabled
title: Option<String>, // Plaintext countdown title; bound via title_hash
reply_to: Option<[u8; 32]>,// Parent qub_id; V2-signed when authorship is enabled
outcome_at: Option<i64>, // Optional future judgment time; bound to qub_id
status: DraftStatus, // Composing | Sealed | Uploaded | Failed
}
2.2 QubEnvelope (ข้อมูลโหลดที่ถอดรหัสแล้ว)
เรียงลำดับบิตโดยใช้ CBOR มาตรฐาน (§3) ถูกเข้ารหัสไว้ภายใน SealedQub นี่คือโครงสร้างที่พิสูจน์ความสมบูรณ์ของเนื้อหาหลังการถอดรหัส
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>, // When reality renders judgment; bound to qub_id
sender_label: Option<String>, // Not in qub_id; V2-signed when authorship is enabled
reply_to: Option<[u8; 32]>,// Parent qub_id; not in qub_id; V2-signed when present
body: Vec<u8>, // UTF-8 text or canonical CBOR pact/verdict body
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
}
เกณฑ์พื้นฐาน (ข้อความ qub ที่ไม่เซ็นชื่อ): version = 0x01, content_type = 0x01, sig_alg = 0x00; ไม่มีช่องสำหรับลายเซ็นและผู้ค้ำประกัน ช่องข้อมูลเมตาอื่น ๆ ที่เป็นทางเลือกอาจปรากฏอยู่
การตั้งค่า v1 อื่น ๆ: content_type = 0x03 (เนื้อหาสัญญา ดู §6.1); sig_alg = 0x01 (ML-DSA-65) โดยมี author_signature และ author_pubkey ปรากฏอยู่ (ดู §9.3); cosigner_pubkey และ cosigner_signature ปรากฏร่วมกันสำหรับสัญญาที่ลงนามร่วม (ดู §9.7); reply_to ตั้งค่าเป็น qub_id ของ qub แม่สำหรับ qub แบบสายตอบกลับ (ดู §9.3 สำหรับผลกระทบด้านขอบเขตของลายเซ็น)
2.3 SealedQub (รูปแบบการสื่อสารมาตรฐาน)
การจัดเรียงลำดับแบบอนุกรมโดยใช้ CBOR แบบมาตรฐาน (§3) นี่คือชิ้นส่วนข้อมูลภายในสายสัญญาณ: การจัดส่งสาธารณะเก็บไบต์เหล่านี้โดยตรง ในขณะที่การจัดส่งส่วนตัวห่อหุ้มมันอยู่ใน OuterWrapper ก่อนเก็บรักษา (§13)
SealedQub {
version: u8, // Protocol major version (0x01 for v1)
qub_id: [u8; 32], // Same as QubEnvelope.qub_id
visibility: u8, // 0x00 = private/wrapped; 0x01 = public/bare
unlock_at: i64, // Unix seconds UTC
outcome_at: Option<i64>, // 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
drand_chain_version: Option<u8>, // W3 — chain-migration version. Absent / 0 = quicknet
// (the only chain today). Lets a future chain swap
// be expressed on the wire without a breaking format
// change. NOT part of the §4.1 qub_id preimage, so its
// addition never alters an existing qub's identity.
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 hostile/control code points.
}
2.4 RevealedQub (สถานะแอปพลิเคชันของผู้ชม)
ไม่ได้เรียงลำดับบิตเป็น CBOR อยู่เฉพาะในแอปผู้ชม สร้างขึ้นหลังจากการถอดรหัสและการตรวจสอบสำเร็จ
RevealedQub {
qub_id: [u8; 32],
arweave_tx_id: String,
visibility: u8,
content_type: u8,
created_at: i64,
unlock_at: i64,
outcome_at: Option<i64>, // Carried from both wire layers; drives the verdict-watch block
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. โปรไฟล์ CBOR มาตรฐาน
การเรียงลำดับบิต SealedQub และ QubEnvelope ทั้งหมดต้องเป็นไปตามโปรไฟล์นี้ การใช้งานสองแบบที่ได้รับโครงสร้างเชิงตรรกะเดียวกันต้องสร้างไบต์ที่เหมือนกัน
3.1 กฎการเข้ารหัส
| กฎ | ข้อกำหนด |
|---|---|
| มาตรฐาน | RFC 8949 §4.2.1 (ข้อกำหนดการเข้ารหัสแบบกำหนดได้แกนกลาง) |
| การเรียงลำดับกุญแจ map | เรียงตาม ความยาวไบต์ที่เข้ารหัสแล้ว ก่อน (สั้นก่อนยาว) แล้วจึง เรียงตามพจนานุกรม (เปรียบเทียบไบต์ต่อไบต์สำหรับการเข้ารหัสที่มีความยาวเท่ากัน) |
| การเข้ารหัสจำนวนเต็ม | รูปแบบสั้นที่สุด: 0–23 ในไบต์เริ่มต้น; 24–255 ใน 2 ไบต์; 256–65535 ใน 3 ไบต์ และอื่น ๆ |
| การเข้ารหัสความยาว | ความยาวแบบกำหนดได้เท่านั้น ไม่อนุญาตอาร์เรย์ map ไบต์สตริง หรือสตริงข้อความที่มีความยาวไม่กำหนด (additional info = 31 ถูกห้าม) |
| แท็ก | ไม่มีแท็ก CBOR (major type 6 ถูกห้าม) |
| ทศนิยมลอย | ไม่มี float (major types 7 values 0xF9–0xFB ถูกห้าม) |
| สตริงข้อความ | เข้ารหัส UTF-8 นอร์มัลไลซ์ NFC (Unicode Normalization Form C) |
| สตริงไบต์ | ไบต์ดิบ ไม่มีการเข้ารหัส base64 ที่ชั้น CBOR |
| กุญแจซ้ำ | ปฏิเสธพร้อมข้อผิดพลาด ตัวแยกวิเคราะห์ต้องไม่ยอมรับกุญแจ map ซ้ำอย่างเงียบ ๆ |
| กุญแจที่ไม่รู้จัก | ปฏิเสธพร้อมข้อผิดพลาด ตัวแยกวิเคราะห์ต้องไม่ยอมรับกุญแจ map นอกชุดกุญแจมาตรฐานของประเภทนั้น — สตริงไบต์มาตรฐานที่แตกต่างกันสองสตริงต้องไม่ถอดรหัสไปเป็นค่าเดียวกัน (encode(decode(x)) == x) และสำหรับ payload ที่ลงนาม กุญแจส่วนเกินจะเป็นเนื้อหาที่ซ่อนอยู่ซึ่งลายเซ็นทั้งสองผูกพันด้วย วิวัฒนาการของสคีมาดำเนินผ่าน version ไม่เคยผ่านกุญแจส่วนเกิน |
| ค่าเรียบง่าย | อนุญาตเฉพาะ true (0xF5), false (0xF4) และ null (0xF6) เท่านั้น |
| ฟิลด์ที่เลือกได้ | ฟิลด์ที่เลือกได้ที่ไม่มีอยู่จะถูก ละเว้น จาก map CBOR ทั้งหมด (ไม่ได้เข้ารหัสเป็น null) ฟิลด์ที่เลือกได้ที่มีอยู่จะรวมอยู่ในลำดับกุญแจที่เรียงแล้ว |
3.2 ลำดับกุญแจมาตรฐานที่ตรวจสอบแล้ว
ลำดับกุญแจเหล่านี้เป็นเชิงบรรทัดฐาน การใช้งานต้องส่งกุญแจในลำดับนี้พอดี การ assertion ระหว่างดีบักควรตรวจสอบลำดับในบิลด์ที่ไม่ใช่รีลีส
QubEnvelope (เวอร์ชัน 0x01, ไม่ลงนาม, ฟิลด์ที่เลือกได้ทั้งหมดไม่มีอยู่):
"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 (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)
การหาลำดับกุญแจของ QubEnvelope: กุญแจแต่ละตัวเป็น CBOR text string ความยาวที่เข้ารหัส = 1 ไบต์เฮดเดอร์ + ความยาวสตริง (สำหรับสตริงต่ำกว่า 24 ไบต์) เรียงตามความยาวที่เข้ารหัสรวมก่อน แล้วจึงเรียงตามพจนานุกรมสำหรับกุญแจที่มีความยาวเท่ากัน
SealedQub (เวอร์ชัน 0x01, สาธารณะ, ไม่มีผู้รับ):
"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 (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)
"drand_chain_version" (20 encoded bytes) ← only if present (W3; absent = quicknet)
PactTerms (เนื้อหาสัญญา, 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 (แถวของอาร์เรย์ 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 อ้างอิงการเข้ารหัสไบต์
| ประเภท | การเข้ารหัส CBOR | ตัวอย่าง |
|---|---|---|
| แฮช SHA3-256 (32 ไบต์) | 0x58 0x20 + 32 ไบต์ |
body_hash, qub_id |
| ไทม์สแตมป์ (i64) | Major type 0 (บวก) หรือ 1 (ลบ) การเข้ารหัสสั้นที่สุด | วินาที Unix |
| เวอร์ชัน (u8, ค่า 1) | 0x01 (ไบต์เดียว) |
|
| ประเภทเนื้อหา (u8, ค่า 1) | 0x01 (ไบต์เดียว) |
|
| sig_alg (u8, ค่า 0) | 0x00 (ไบต์เดียว) |
|
| ลายเซ็น ML-DSA-65 (3,309 ไบต์) | 0x59 0x0C 0xED + 3,309 ไบต์ |
author_signature, cosigner_signature |
| กุญแจสาธารณะ ML-DSA-65 (1,952 ไบต์) | 0x59 0x07 0xA0 + 1,952 ไบต์ |
author_pubkey, cosigner_pubkey |
4. การหาค่าเชิงบรรทัดฐาน
4.1 qub_id
qub_id ระบุ qub เฉพาะตัวและผูก QubEnvelope เข้ากับ SealedQub มันถูกหาได้แบบกำหนดได้จากเนื้อหาของซอง
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
การเข้ารหัสตัวแยกโดเมน: สตริง "QUB_ID_V2" คือ 9 ไบต์ ASCII มีการเพิ่มไบต์ padding 0x00 หนึ่งไบต์เพื่อให้ครบ 10 ไบต์เพื่อการจัดเรียง การใช้งานต้องใช้ 10 ไบต์เหล่านี้พอดี: [0x51, 0x55, 0x42, 0x5F, 0x49, 0x44, 0x5F, 0x56, 0x32, 0x00]
outcome_at การเข้ารหัส: การปรับปรุงเวอร์ชันทดลองก่อนการปล่อยได้ขยายค่าพรีอิมเมจจาก 92 เป็น 100 ไบต์เพื่อรวมตัวเลือก outcome_at ช่องข้อมูลเข้าสู่การเชื่อมโยง ขาด outcome_at ถูกเข้ารหัสเป็นไบต์ศูนย์ 8 ไบต์; ตัวตรวจสอบโปรโตคอลปฏิเสธ outcome_at <= 0 ทุกที่ ดังนั้นผู้เฝ้ายามนี้จึงไม่สามารถชนกับค่าที่ถูกต้องได้ ดู §3.2 (รูปแบบสาย) และในต้นไม้ tasks/verdict-uplift-plan.md สำหรับกลไกคำตัดสินที่กระตุ้นสาขานี้
drand_round การเข้ารหัส: การปรับปรุงเวอร์ชันทดลองก่อนวางจำหน่ายในภายหลังได้ขยายพรีอิมเมจจาก 100 ไบต์เป็น 108 ไบต์เพื่อนำมาพับ drand_round (ดรันด์เป้าหมายรอบ, §4.3) ลงในการผูกมัด และปรับตัวคั่นโดเมนขึ้นเป็น QUB_ID_V2. สิ่งนี้ผูกมัดรอบ timelock เข้ากับตัวตน qub: เกตเวย์ไม่สามารถผูกข้อความเข้ารหัสกับรอบอื่น (เช่น รอบที่ผ่านไปแล้ว) ที่แตกต่างจากที่แสดงได้ unlock_at บ่งชี้ ขั้นตอนการปลดล็อก (§8) ยังตรวจสอบเพิ่มเติมว่ารอบที่ฝังอยู่ในท่อนรหัสลับ tlock ตรงกันหรือไม่ unlock_round(unlock_at), ดังนั้นเวลาที่ปลดล็อกที่แสดงจึงสามารถพิสูจน์ได้ว่าเป็นรอบที่เปิดการถอดรหัส
คุณสมบัติ:
- การเปลี่ยนแปลงฟิลด์ใด ๆ ที่ถูกผูกโดยภาพต้นฉบับ—
version,content_type,created_at,unlock_at,outcome_at,drand_round, ดิบbodyไบต์ (ผ่านbody_hash), หรือtitle(ผ่านtitle_hash)—ผลิตผลที่แตกต่างqub_id. - qub_id จะถูกคำนวณก่อนการเข้ารหัส ทั้ง QubEnvelope และ SealedQub มี qub_id เหมือนกัน ผู้ดูจะตรวจสอบว่าตรงกันหลังจากการถอดรหัส
qub_idไม่ขึ้นอยู่กับsender_label,reply_to, ไบต์ลายเซ็น หรือกุญแจสาธารณะสำหรับการลงนาม อย่างไรก็ตาม ภายใต้โครงสร้างการลงนาม V2 ปัจจุบันsender_labelและreply_toได้รับการยืนยันตัวตนโดยตรงโดยsender_label_hashและreply_to_or_zero(§9.3) ทุกครั้งที่มีลายเซ็นปรากฏ- การเปลี่ยนแปลง SealedQub
title(เมื่อทุกอย่างอย่างอื่นได้รับการแก้ไขแล้ว) การเปลี่ยนแปลงqub_idผ่านtitle_hash. ดังนั้นเกตเวย์จึงไม่สามารถสลับชื่อเรื่องแบบข้อความธรรมดาที่แสดงบนตัวนับถอยหลังได้โดยไม่ทำให้ตัวตนของ qub เป็นโมฆะ - การเปลี่ยนแปลง SealedQub
outcome_at(เมื่อทุกอย่างอย่างอื่นได้รับการแก้ไขแล้ว) การเปลี่ยนแปลงqub_idผ่านภาพล่วงหน้า เกตเวย์ไม่สามารถสลับวันที่ตัดสินก่อนการเปิดเผยที่แสดงบนตัวนับถอยหลังได้โดยไม่ทำให้ตัวตน qub ไม่ถูกต้อง - กำลังเปลี่ยน
drand_round(เมื่อทุกอย่างอย่างอื่นได้รับการแก้ไขแล้ว) การเปลี่ยนแปลงqub_idผ่านภาพย้อนกลับ เกตเวย์ไม่สามารถผูกรหัสลับ timelock เข้ากับรอบที่ต่างออกไปได้โดยไม่ทำให้เอกลักษณ์ qub ไม่ถูกต้อง; รวมกับการตรวจสอบรอบคาถาเวลาปลดล็อก §8 ที่แสดงunlock_atคือรอบที่จริง ๆ แล้วควบคุมการถอดรหัส
4.2 body_hash
body_hash = SHA3-256(body)
โดยที่ body คือข้อมูลโหลดเนื้อหา Vec<u8> ดิบ สำหรับ qub ข้อความ นี่คือเนื้อหา qub ที่เข้ารหัส 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
โดยที่ title คือชื่อข้อความธรรมดาที่เลือกได้ซึ่งแสดงในการนับถอยหลังของผู้ชมก่อนการเปิดเผย (ดู §3.2) การนอร์มัลไลซ์ NFC ทำงานในเวลาแฮชเพื่อให้ไดเจสต์มีเสถียรภาพข้ามลำดับโค้ดพอยต์ที่เทียบเท่ากันทางการมองเห็น ค่า sentinel ที่เป็นศูนย์ทั้งหมดถูกสงวนไว้สำหรับกรณีไม่มี สตริงว่างถูกปฏิเสธที่ขอบเขต CBOR มาตรฐานเนื่องจากเป็นการเข้ารหัส "ไม่มี" ที่ไม่เป็นมาตรฐาน (การเข้ารหัสมาตรฐานละเว้นฟิลด์ทั้งหมด)
4.3 การแมปการปลดล็อก-รอบ
drand_round = floor((unlock_at - chain_genesis_time) / chain_period_seconds) + 1
| พารามิเตอร์ | แหล่งที่มา | ตัวอย่าง |
|---|---|---|
unlock_at |
วินาที Unix ตาม UTC ที่ผู้ใช้เลือก | 1735689600 (2025-01-01 00:00:00 UTC) |
chain_genesis_time |
ข้อมูลเชน drand (genesis_time) |
1595431050 |
chain_period_seconds |
ข้อมูลห่วงโซ่ drand (period) |
30 |
นี่คือการแมปล็อกอ้างอิง (drand's) CurrentRound). drand เผยแพร่รอบ N ที่ chain_genesis_time + (N - 1) * chain_period_seconds, ดังนั้นสูตรจะเลือก กระแสรอบที่ unlock_at — รอบที่ลายเซ็นของมันเป็นลายเซ็นแรกที่ผู้ชมมาถึง unlock_at สามารถใช้ได้
คุณสมบัติการจัดแนว (กรณีที่มีความสำคัญในทางปฏิบัติ): เมื่อ (unlock_at - chain_genesis_time) หารลงตัวพอดีด้วย chain_period_seconds, ลายเซ็นของรอบที่เลือกจะถูกเผยแพร่ ตรงที่ unlock_at, ไม่เคยมาก่อน. สิ่งนี้ใช้ได้เสมอสำหรับการติดตั้งอ้างอิง: เวลาเริ่มต้นของ quicknet (1692803367) หารด้วยช่วงเวลา 3 วินาทีของมันลงตัว และแอปอ้างอิงล็อกเวลาปลดล็อกเป็นนาทีเต็ม สำหรับที่ไม่ตรงกัน unlock_at, ลายเซ็นของรอบที่เลือกเผยแพร่น้อยกว่าหนึ่งช่วงเวลาอย่างเคร่งครัดก่อน unlock_at — ความแม่นยำทางเวลาของการมุ่งมั่นคือหนึ่งช่วงสัญญาณไฟสัญญาณ
การแมปก่อนเปิดตัวแบบเก่าและความทนต่อฝั่งการปลดล็อก: การแมปต้นฉบับคือ ceil((unlock_at - chain_genesis_time) / chain_period_seconds), ซึ่ง—สำหรับกรณีที่สอดคล้องกับช่วงเวลาข้างต้น—ได้เลือกสิ่งที่เผยแพร่รอบเต็มหนึ่งช่วงเวลา ก่อน unlock_at, ทำให้ข้อความเข้ารหัสสามารถถอดรหัสได้ล่วงหน้าตรงหนึ่งช่วงเวลา การแมปทั้งสองแตกต่างกันอย่างแม่นยำ +1 เมื่อเดลต้าแบ่งช่วงเวลา และตกลงแตกต่างกัน เพราะ drand_round ถูกพับเข้าไปในสิ่งที่ไม่สามารถเปลี่ยนแปลงได้ qub_id preimage (§4.1) สิ่งประดิษฐ์ที่ปิดผนึกภายใต้การแมปแบบเก่าไม่สามารถสร้างขึ้นใหม่ได้; ดังนั้นผู้ตรวจสอบที่ทำขั้นตอน §8 ขั้นตอนที่ 6a รอบการตรวจสอบข้ามจึงต้องยอมรับข้อมูลที่เก็บไว้ drand_round เท่ากับ ทั้งคู่ รอบที่ได้มา หรือ รอบที่ได้หักหนึ่ง (และต้องการให้รอบท็อคสแตนซ่าเท่ากับรอบที่จัดเก็บไว้อย่างแม่นยำ) ความคลาดเคลื่อนจะขยายลายเซ็นการควบคุมที่เร็วที่สุดไม่เกินหนึ่งรอบ บริการการจัดเวทีของสัญญาจะใช้ความคลาดเคลื่อนเดียวกันเมื่อมันสร้างใหม่สัญญาที่จัดเวที qub_id (ที่เวทีและที่การร่วมลงนาม): หากรอบปัจจุบันของการทำแผนที่ไม่ทำซ้ำสิ่งที่ได้มอบหมาย qub_id และเดลตาแบ่งช่วงเวลา มันลองใหม่ด้วยรอบลบหนึ่ง และมันปิดสนธิสัญญาที่เสร็จสมบูรณ์ไปยังรอบใดก็ตาม qub_id จริงๆ แล้วผูกมัด—ไม่เคยผูกมัดโดยไม่คิดกับรอบที่คำนวณใหม่ ซึ่งจะทำให้สิ่งประดิษฐ์นั้นไม่สามารถสืบทอดได้อย่างถาวร
การตรวจสอบความถูกต้อง: unlock_at ต้องอยู่ในอนาคตในเวลาประทับตรา unlock_at ต้องไม่เกิน 10 ปี จาก created_at (เพื่อลดความเสี่ยงจากการพึ่งพา drand ระยะยาว; UI ควรแจ้งเตือนสำหรับวันที่ปลดล็อกที่เกิน 2 ปี)
5. Newtype รูปแบบการสื่อสาร
Newtype รูปแบบการสื่อสารให้ความปลอดภัยในเวลาคอมไพล์ป้องกันความสับสนของไบต์ CBOR กับ JSON ข้อความธรรมดาดิบ หรือการเข้ารหัสไบต์อื่น ๆ
| ประเภท | ประกอบด้วย | ผลิตโดย | ถูกครอบงำโดย |
|---|---|---|---|
SealedQubCbor |
CBOR แบบมาตรฐานของ SealedQub | serialize_sealed_qub() |
สิ่งประดิษฐ์สายภายใน; เก็บเปลือยเพื่อส่งมอบต่อสาธารณะหรือห่อเพื่อส่งมอบส่วนตัว แล้วถูกดูดกลับโดยผู้ชม |
QubEnvelopeCbor |
Canonical CBOR ของ QubEnvelope | serialize_qub_envelope() |
tlock เข้ารหัสข้อมูลเข้า, tlock ถอดรหัสข้อมูลออก |
5.1 กฎการสร้าง
// 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 การตรวจสอบเมื่อสร้าง
from_encoded() ควรตรวจสอบว่าอินพุตเริ่มต้นด้วยเฮดเดอร์ map CBOR ที่ถูกต้อง การตรวจสอบโครงสร้างเต็มรูปแบบเกิดขึ้นในเวลาแยกวิเคราะห์ ไม่ใช่ในเวลาสร้าง เพื่อหลีกเลี่ยงการแยกวิเคราะห์ซ้ำ
6. ทะเบียนประเภทเนื้อหา
| ค่า | ประเภท | ขนาด Body สูงสุด | หมายเหตุ |
|---|---|---|---|
0x00 |
สงวน (ไม่ถูกต้อง) | — | ต้องไม่ใช้ |
0x01 |
ข้อความธรรมดา (UTF-8, Markdown จำกัด) | 50 KB ชำระเงิน / 10 KB ฟรี | ดู §10 สำหรับกฎการเรนเดอร์ การแบ่งฟรี / ชำระเงินถูกบังคับใช้โดยบริการอัปโหลด เพดานแข็งระดับโปรโตคอลคือ 50 KB |
0x02 |
สงวน (อนาคต) | — | จัดสรรไว้สำหรับประเภทเนื้อหาในอนาคต; ไม่ถูกต้องใน v1 ผู้ชมต้องปฏิเสธตามกฎด้านล่าง |
0x03 |
สัญญา (ข้อตกลงทวิภาคี, เนื้อหา CBOR) | 100 KB | Body คือ canonical CBOR PactTerms (§6.1) การลงนามผู้ลงนามร่วมตาม §9.7 |
0x04 |
คำตัดสิน (การให้คะแนนตัวเองของผู้สร้าง, เนื้อหา CBOR) | 8 KB | Body คือ canonical CBOR VerdictBody (§6.2) ส่งออกได้เฉพาะ intent ฝั่งระบบ verdict เท่านั้น ความสัมพันธ์กับ qub แม่อยู่บน Arweave tag Parent-Tx-Id ไม่ได้อยู่บน body ดู verdict-uplift-plan §3.4 |
ผู้ชมต้องปฏิเสธประเภทเนื้อหาที่ไม่รู้จักด้วยข้อผิดพลาดที่ผู้ใช้มองเห็นได้ชัดเจน ผู้ชมต้องไม่พยายามเรนเดอร์ประเภทที่ไม่รู้จักเป็นข้อความ
6.1 Body ของสัญญา (content_type = 0x03)
Body ของสัญญาคือการเข้ารหัส CBOR มาตรฐานของค่า 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 bytes), value: String (≤ 2,000 bytes) } // NFC
PartyIdentifier{ label: String (≤ 100 bytes), contact: Option<String (≤ 320 bytes)> }
ลำดับกุญแจ CBOR มาตรฐานสำหรับ map ทั้งสามรายการอยู่ใน §3.2 CBOR สัญญาที่เรียงลำดับบิตทั้งหมดต้องไม่เกิน 100 KB (ตรงกับ §6)
ตัวแยกประเภทสคีมา แถวแรกใน terms สำหรับสัญญา structured/v1 ต้องเป็น { key: "pact_schema", value: "structured/v1" } แถวที่ไม่มีเครื่องหมายนี้เป็นสัญญา "กำหนดเอง" และไม่ได้รับการตรวจสอบเชิงโครงสร้างหรือการเรนเดอร์ที่ตระหนักถึงสคีมา
ช่องการรับทราบที่ถูกตรึง สัญญา structured/v1 มีแถวการรับทราบสี่แถวพอดีภายใต้กุญแจเหล่านี้:
"initiator_standard_terms"
"initiator_capacity_terms"
"counterparty_standard_terms"
"counterparty_capacity_terms"
value สำหรับแต่ละแถวเป็นหนึ่งในแปดสตริงภาษาอังกฤษที่ถูกตรึง ซึ่งเลือกโดยคู่ (role, kind) โดยที่ role ∈ { seller, buyer, provider, client } และ kind ∈ { standard, capacity } สตริงเหล่านี้เป็น ข้อมูลโปรโตคอลเชิงบรรทัดฐาน — ลายเซ็น ML-DSA-65 ของทั้งสองฝ่ายผูกพันกับไบต์ที่แน่นอนผ่าน body_hash สตริงเหล่านี้ไม่ได้แปลเป็นภาษาท้องถิ่น; body ที่ลงนามแล้วเป็นกลางทางภาษา การเปลี่ยนถ้อยคำใด ๆ ต้องมีเวอร์ชันสคีมาใหม่ (structured/v2)
แปดสตริง การค้นหา (acknowledgement_for(role, kind)) และเหตุผลสำหรับแต่ละสตริงถูกตรึงโดยการใช้งานอ้างอิง การใช้งานที่สอดคล้องต้องส่งค่าการรับทราบที่เป็นไบต์เหมือนกัน การทดสอบ golden-fixture SHA3-256 body-hash ที่ครอบคลุมการรวมบทบาททั้งสี่จับการเลื่อนใด ๆ
ลำดับการแสดงผลของผู้ชม สตริงการรับทราบมีวลีเช่น "described above" ซึ่งสันนิษฐานว่าแถวคำอธิบาย / ขอบเขตเรนเดอร์ก่อนการรับทราบ ผู้ชมต้องเรนเดอร์อาร์เรย์ terms ตามลำดับ CBOR; การจัดเรียงใหม่ทำลายความหมายของข้อความ
ผู้ติดต่อของคู่สัญญา เมื่อ contact ของฝ่าย B เป็นที่อยู่อีเมลที่ถูกต้อง บริการอัปโหลด qub จะส่งอีเมลคำเชิญรีวิว / ลงนามร่วมโดยอัตโนมัติในเวลาจัดเตรียม และผูกการลงนามร่วมเอาในที่สุดเข้ากับการยืนยันที่อยู่เดียวกันนั้น (§9.7) สัญญาที่ผู้ติดต่อฝ่าย B ไม่มีอยู่ยังสามารถลงนามร่วมได้ แต่ผ่านช่องทางนอกแบนด์เท่านั้น — บริการปฏิเสธคำขอลงนามร่วมที่ไม่สามารถสร้างเครื่องหมายยืนยันอีเมล 15 นาทีที่ตรงกันได้
6.2 Body ของคำตัดสิน (content_type = 0x04)
Body ของคำตัดสินคือการเข้ารหัส canonical CBOR ของค่า 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
}
ลำดับกุญแจ canonical CBOR:
"outcome" (8 encoded bytes)
"reflection" (11 encoded bytes) ← only if present
"evidence_url" (13 encoded bytes) ← only if present
"verdict_version" (16 encoded bytes)
CBOR ของคำตัดสินที่ทำให้เป็นอนุกรมทั้งหมดต้องไม่เกิน 8 KB (ตรงกับแถวรีจิสทรีด้านบน)
Enum ของผลลัพธ์ ไบต์บนสายเป็นกลางทางเจตนา; สี่กลุ่ม Right / Partial / Wrong / Unfalsifiable ครอบคลุมพื้นที่ผลลัพธ์ของทุก intent ที่มีคำตัดสิน ป้ายเฉพาะ intent ("Called it" / "Kept it" / "Shipped" / "Confirmed" สำหรับ Right เป็นต้น) เป็นเรื่องการเรนเดอร์ฝั่งผู้ชมที่แก้ไขเทียบกับ intent ของ qub แม่ — สายยังคงเป็นกลางทางภาษาและ intent ค่าที่อยู่นอก 1..=4 ต้องถูกปฏิเสธในขั้นตอนถอดรหัส
การเชื่อมโยงกับ qub แม่ qub ของคำตัดสินไม่ได้ใส่การอ้างอิงไปยัง qub แม่ใน body รหัสธุรกรรม Arweave ของ qub แม่จะส่งออกเป็นแท็กการจัดเก็บ Parent-Tx-Id ในเวลาอัปโหลด (§7 ชั้นแท็กการจัดเก็บ) วิธีนี้ทำให้ body เป็นคำกล่าวที่ลงนามครบในตัวเองของการประเมินตนเอง ห่วงโซ่ตรวจสอบ ("ทำนายอะไรถูก") ถูกสร้างขึ้นผ่านการค้นหาแท็ก Arweave
ความปลอดภัยของ URL หลักฐาน (เชิงบรรทัดฐาน) เมื่อมี evidence_url อยู่ ตัวตรวจสอบ (ฝั่ง compose, ฝั่งสาย, ขอบ Worker) ต้องบังคับใช้:
- HTTPS เท่านั้น สตริงต้องขึ้นต้นด้วยลำดับไบต์
https://สคีมาอื่นใด —http,ftp,javascript,data,fileฯลฯ — จะถูกปฏิเสธ - เพดานความยาว ≤ 2,048 ไบต์ (ขีดจำกัด URL เชิงปฏิบัติของเบราว์เซอร์)
- ตรวจ NFC + จุดรหัสที่เป็นอันตราย กฎเดียวกับ
titleและreflection— จุดรหัส bidi-override / zero-width / tag-block / BOM / C0 / C1 จะถูกปฏิเสธ คำจำกัดความตรงกับ Rustcrate::handle::contains_hostile_text_codepointและ TSworkers/api/src/utils/unicode.ts::isHostileCodepoint(รักษาให้สอดคล้องกัน) - ไม่มีช่องว่าง ไม่มี ASCII control ช่องว่าง / DEL / ไบต์ต่ำกว่า
0x20ที่ใดก็ตามใน URL จะถูกปฏิเสธ — ปิดช่องโหว่การฉีด\n/\tที่กฎ bidi ไม่ครอบคลุม - ส่วน host ต้องไม่ว่างเปล่า ทุกอย่างระหว่าง
https://กับ/,?, หรือ#ตัวแรกต้องไม่ว่างเปล่า
ไม่มีการดึงข้อมูลฝั่งเซิร์ฟเวอร์ Worker ต้องไม่ proxy, fetch, หรือพรีวิว URL โปรโตคอลเก็บเพียงสตริง การเรนเดอร์เกิดขึ้นฝั่งผู้ชมด้วย rel="nofollow noopener noreferrer" target="_blank" และแสดงโฮสต์ที่มองเห็นได้ข้างข้อความลิงก์
การสะท้อนคิด ข้อความสะท้อนคิดที่ผู้สร้างเขียนไว้ตามทางเลือก ("อะไรเปลี่ยนไป คุณเรียนรู้อะไร") การตรวจสอบ NFC + จุดรหัสที่เป็นอันตรายเดียวกับ title อินพุตที่ว่างเปล่า / มีแต่ช่องว่างจะยุบเป็นไม่มีในเวลาก่อสร้าง
เวอร์ชันสคีมา v1 รองรับเฉพาะ verdict_version = 0x01 การแก้ไขสคีมาในอนาคตจะเพิ่มไบต์นี้และมาพร้อมเวอร์ชันโปรโตคอลใหม่ตาม §12
7. โปรโตคอลผนึก
ลำดับการผนึกที่สมบูรณ์ แต่ละขั้นตอนเป็นเชิงบรรทัดฐาน
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.
f. visibility is 0x00 (private) or 0x01 (public).
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 = floor((unlock_at - chain_genesis_time) / chain_period_seconds) + 1
(§4.3). (Computed here, before qub_id, because drand_round is bound into the
qub_id preimage—§4.1.)
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,
visibility, unlock_at, drand_chain_id, and drand_round.
11. Serialise SealedQub using canonical CBOR → SealedQubCbor.
12. Select the delivery shape from visibility:
a. Private (0x00): generate K = 32 random bytes and N = 12 random bytes
using a CSPRNG. Compute W = wrap_sealed_qub(SealedQubCbor,
qub_id=qub_id, key=K, nonce=N) per §13. Upload payload = W.
b. Public (0x01): upload payload = bare SealedQubCbor; do not generate K.
13. Display seal-time disclosure. User confirms.
14. Validate upload eligibility via the qub upload service (bot-detection, entitlement, rate limits).
15. Submit the selected upload payload to the qub upload service. For a private
browser seal, the service is byte-blind to the inner SealedQubCbor and never
receives K. The Builder `/api/v1/seal` route is an explicit exception: it
receives plaintext and caller-supplied K in memory, then persists neither.
16. Receive arweave_tx_id from the service. For private delivery, construct
`<origin>/c/<arweave_tx_id>#<base64url(K)>` (or the equivalent short-code
path). For public delivery, omit the fragment. Browsers do not transmit URL
fragments to servers, so K from the browser-seal path is not observed by
qub.social or any storage gateway.
ชั้นแท็กการจัดเก็บ (out-of-band) บริการอัปโหลด qub จะแนบชุดแท็กธุรกรรมการจัดเก็บขนาดเล็กโดยเจตนาพร้อมกับข้อมูลอัปโหลดที่เลือก Content-Type=application/octet-stream เป็นสิ่งที่ต้องทำตามเกณฑ์ นอกจากนี้ บริการอ้างอิงยังแนบแท็กทางเลือกสามรายการเมื่อผู้สร้างเลือกที่จะเผยแพร่พวกมัน: Intent (ตั้งใจสร้างที่ได้รับการตรวจสอบจากรายการอนุญาต—announcement, thesis, prediction, letter, secret, commitment, proof, หรือปล่อยโดยระบบ verdict), Author (ลายนิ้วมือ pubkey §9.3 ของผู้สร้างเป็นเลขฐานสิบหกตัวพิมพ์เล็ก 64 หลัก), และ Parent-Tx-Id (รหัสธุรกรรมการจัดเก็บของ qub แม่สำหรับโซ่การตอบกลับ, base64url 43 ตัวอักษร)
นั้น Author แท็กคือ เลือกเข้าร่วมต่อ qub: แอปสร้างการอ้างอิงจะแนบสิ่งนี้เฉพาะเมื่อผู้ใช้เปิดใช้งานการระบุแหล่งที่มาแบบสาธารณะอย่างชัดเจนในเวลาประทับตรา เมื่อสวิตช์ปิดอยู่ — ซึ่งเป็นค่าเริ่มต้น — จะไม่มี Author แท็กถูกเขียนและ qub ไม่ได้ระบุแหล่งที่มาบนเชน: ไม่มีอะไรในที่เก็บถาวรเชื่อมโยงการอัปโหลดกับชื่อผู้สร้าง อีเมล หรือ qubs อื่น ๆ เมื่อสวิตช์ถูกเปิด Author ลายนิ้วมือชี้ไปยังสิ่งที่ผู้สร้างเลือก @handle ผ่านห่วงโซ่การรับรอง §9.5 ความสัมพันธ์ของห่วงโซ่การตอบกลับและ Intent ไม่ใช่การระบุ สำหรับการส่งแบบส่วนตัว ชั้นนอกของบรรจุภัณฑ์ (§13) เข้ารหัสชั้นในที่สามารถจดจำได้ SealedQub สิ่งประดิษฐ์ดังนั้นการเก็บรวบรวมตัวห่อที่จัดเก็บไว้และการได้รับลายเซ็นดรานด์สาธารณะยังไม่เพียงพอที่จะกู้คืนร่างกายโดยปราศจาก K; แท็กการจัดเก็บยังคงเป็นเมตาดาต้าสาธารณะโดยเจตนา
บริการอ้างอิงจงใจไม่แนบแท็ก App-Name, App-Version หรือ Type: ตัวกรองค่าเดียวใด ๆ จะส่งคืนคลังข้อมูล qub ทั้งหมดให้กับการสอบถาม GraphQL ซึ่งไม่สอดคล้องกับขอบเขตการรักษาความลับเฉพาะ body ของตัวห่อหุ้ม
ตัวตรวจสอบที่สอดคล้องต้องไม่พึ่งพาแท็กการจัดเก็บใด ๆ สำหรับการตรวจสอบของบุคคลที่สามตาม §11; body hash / qub_id / ลายเซ็นผูกพันกับ CBOR ภายในเท่านั้น ไม่เคยผูกกับชุดแท็ก
8. โปรโตคอลปลดล็อก
ลำดับการปลดล็อกที่สมบูรณ์ แต่ละขั้นตอนเป็นเชิงบรรทัดฐาน
1. Viewer opens delivery URL. Extract arweave_tx_id from the path and retain
the optional URL fragment. Do not assume a missing fragment is an error:
public/bare delivery intentionally has no K.
2. Check denylist. If tx_id is denylisted → display block message. Stop.
3. Fetch the stored bytes (with multi-gateway fallback).
3a. Resolve the delivery shape structurally:
a. If the bytes parse as OuterWrapper, require a well-formed 32-byte K in
the URL fragment, require wrapper version 0x01, and unwrap per §13.
Any missing/malformed K or AEAD failure is a terminal error.
b. Otherwise require the bytes to parse as bare SealedQubCbor; no K is
required. If neither shape parses, report an integrity error.
4. Parse SealedQubCbor → SealedQub.
5. Validate: SealedQub.version is known (0x01), visibility is known, and the
delivery shape matches it (wrapped = 0x00 private; bare = 0x01 public).
Reject any mismatch or unknown value.
6. If current time < SealedQub.unlock_at → display countdown. Poll or wait.
6a. Round-binding check. Recompute expected_round from
SealedQub.unlock_at per §4.3. Reject unless SealedQub.drand_round ==
expected_round OR SealedQub.drand_round == expected_round - 1 (the
legacy pre-release mapping—see §4.3), AND the round baked into the tlock
ciphertext stanza (read via the age/tlock header, no signature required)
== SealedQub.drand_round exactly. 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.
12a. Verify: QubEnvelope.outcome_at == SealedQub.outcome_at (both absent, or
both present and equal). Fail → integrity error.
12b. Content re-derivation. Recompute qub_id per §4.1 from the decrypted
fields — (QubEnvelope.version, content_type, created_at, unlock_at,
outcome_at, SealedQub.drand_round, QubEnvelope.body_hash,
title_hash(SealedQub.title)) — and verify it equals SealedQub.qub_id.
Fail → integrity error. The pairwise checks in steps 10-12a only prove
the two layers agree with EACH OTHER; a forger who rewrites a bound
field consistently on both surfaces (a pre-reveal title swap, or a
post-round body swap with a recomputed body_hash re-encrypted to the
same round under the same qub_id) passes them all. Only re-deriving
the identity from content closes this.
13. Verify: QubEnvelope.content_type is known and renderable.
Known values: 0x01 (text), 0x03 (pact), 0x04 (verdict).
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 the appropriate renderer (see §10 for text and §6 for pact/verdict).
17. Construct RevealedQub for display.
9. การลงนามผู้เขียน
9.1 เหตุผล
qubs ถูกจัดเก็บในพื้นที่เก็บถาวร ลายเซ็นผู้เขียนต้องไม่สามารถปลอมแปลงได้อย่างไม่มีกำหนด ซึ่งเป็นเหตุผลที่ v1.0 ใช้รูปแบบหลังควอนตัม ML-DSA-65 (FIPS 204) แทนรูปแบบคลาสสิกที่ความปลอดภัยอาจเสื่อมลงภายในอายุการใช้งานถาวรของ qub
9.2 ทะเบียนอัลกอริทึม
sig_alg |
แผนการ | ขนาดกุญแจ | ขนาดลายเซ็น | สถานะ |
|---|---|---|---|---|
0x00 |
ไม่มีลายเซ็น (ไม่ได้ลงนาม) | — | — | ใช้งานอยู่ |
0x01 |
ML-DSA-65 (FIPS 204) | 1,952 ไบต์ | 3,309 ไบต์ | ใช้งานอยู่ |
0x02 |
Ed25519 | 32 ไบต์ | 64 ไบต์ | ค่าคงที่ที่สงวนไว้; ไม่รองรับในโปรโตคอล v1 |
ผู้ชม Protocol-v1 ต้องปฏิเสธทุกค่าที่อยู่นอกเหนือ {0x00, 0x01}, รวมถึง
ผู้ที่เก็บตัว 0x02 ค่า การจองป้องกันการใช้งานซ้ำโดยไม่ได้ตั้งใจ; มันไม่ใช่
การเปิดใช้งาน การเปิดใช้งานนั้นต้องการการเปลี่ยนแปลงที่ควบคุมดังที่ระบุใน §15
9.3 การสร้างพรีอิมเมจที่ลงนาม
พรีอิมเมจมีอยู่สองเวอร์ชัน ลายเซ็นทั้งหมดต้องใช้ V2 และตัวตรวจสอบต้องยอมรับ V2 เท่านั้น พรีอิมเมจ V1 ดั้งเดิม (บันทึกไว้ด้านล่างเพื่อการอ้างอิงเชิงประวัติ) เคยได้รับการยอมรับเป็นค่าสำรองเฉพาะการตรวจสอบระหว่างการย้ายไปยัง V2 ค่าสำรองนั้นถูกยกเลิกแล้ว และลายเซ็นที่ใช้ V1 เท่านั้นจะถูกปฏิเสธในตอนนี้
V2 (ปัจจุบัน — สร้างโดยการลงนามผู้เขียนใหม่ทั้งหมด และโดยลายเซ็นทั้งสองของขั้นตอนการจัดเตรียม / ลงนามร่วมของสัญญา):
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 ใช้แบบแผนค่าเซนติเนลสำหรับกรณีไม่มีค่าเดียวกันกับ title_hash (§4.2.1): 32 ไบต์ศูนย์ไม่ใช่เอาต์พุต SHA3-256 ที่ถูกต้อง ดังนั้น "ไม่มีค่า" จะไม่มีวันชนกับป้ายที่มีอยู่ ทุกฟิลด์มีความกว้างคงที่ ดังนั้นพรีอิมเมจจึงไม่กำกวมโดยไม่ต้องมีคำนำหน้าความยาว
V1 (ดั้งเดิม — ยกเลิกแล้ว; ไม่ถูกสร้างขึ้นอีกต่อไปและไม่ได้รับการยอมรับในการตรวจสอบอีกต่อไป):
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
พรีอิมเมจ V1 ละเว้น sender_label และ reply_to มันเคยได้รับการยอมรับเป็นค่าสำรองเฉพาะการตรวจสอบระหว่างการย้ายไปยัง V2 ค่าสำรองนั้นถูกยกเลิกไปแล้ว — ตัวตรวจสอบต้องยอมรับพรีอิมเมจ V2 เท่านั้น นิยามนี้ยังคงไว้ที่นี่เพื่อการอ้างอิงเชิงประวัติและเพื่ออธิบายตัวแยกโดเมนด้านล่าง ลายเซ็นที่ตรวจสอบผ่านกับ V1 เท่านั้นต้องถือว่าเป็นการตรวจสอบที่ล้มเหลว
ตัวแยกโดเมน: "QUB_AUTHOR_SIG_V1" / "QUB_AUTHOR_SIG_V2" คือ 17 ไบต์ ASCII แต่ละตัว ([0x51, 0x55, 0x42, 0x5F, 0x41, 0x55, 0x54, 0x48, 0x4F, 0x52, 0x5F, 0x53, 0x49, 0x47, 0x5F, 0x56, 0x31/0x32]) ไม่มี padding ตัวแยกที่ต่างกันแยกโดเมนของโครงสร้างทั้งสอง ดังนั้นลายเซ็นบนพรีอิมเมจหนึ่งจะไม่มีวันตรวจสอบผ่านเป็นอีกอันหนึ่งได้
ไบต์ org_id_present: ไบต์ที่ตามหลัง unlock_at ต้องเป็น 0x00 การใช้งานอ้างอิงเปิดเผยสิ่งนี้เป็นค่าคงที่ ORG_ID_PRESENT_INDIVIDUAL = 0x00 ใน crates/qub-core/src/signing.rs; ผู้ชมที่สร้าง sig_input ใหม่สำหรับการตรวจสอบต้องส่งไบต์เดียวกัน
ขอบเขตลายเซ็น — สิ่งที่ครอบคลุมและไม่ครอบคลุม sig_input ของ V2 ผูกพันโดยตรงกับ version, qub_id, body_hash, unlock_at, sender_label และ reply_to (บวกตัวแยกโดเมนคงที่และไบต์ org_id_present) qub_id เองได้มาจาก version, content_type, created_at, unlock_at, outcome_at, drand_round และ body_hash ผ่านพรีอิมเมจ §4.1 ดังนั้นการเปลี่ยนแปลงฟิลด์เหล่านั้นใด ๆ จะสร้าง qub_id ที่แตกต่างกันและทำให้ลายเซ็นไม่ถูกต้องโดยอ้อม พื้นผิวที่รับรองความถูกต้องจึงเป็น:
| สนาม | รับรองโดยลายเซ็น | อย่างไร |
|---|---|---|
version |
✓ | ป้อนโดยตรงไปยัง sig_input |
qub_id |
✓ | ป้อนข้อมูลโดยตรง |
body_hash |
✓ | ป้อนข้อมูลโดยตรง |
unlock_at |
✓ | ป้อนข้อมูลโดยตรง |
sender_label |
✓ | การป้อนข้อมูลโดยตรงผ่าน sender_label_hash (V2 พรีอิมเมจ — รูปแบบเดียวที่ได้รับการยอมรับ) |
reply_to |
✓ | การป้อนข้อมูลโดยตรงผ่าน reply_to_or_zero (V2 พรีอิมเมจ — รูปแบบเดียวที่ได้รับการยอมรับ) |
content_type |
✓ | แบบทางอ้อม, ผ่าน qub_id พรีอิมเมจ |
created_at |
✓ | แบบกริยาสอดแทรก ผ่าน qub_id พรีอิมเมจ |
outcome_at |
✓ | แบบกริยาสอดคล้องผ่าน qub_id พรีอิมเมจ |
drand_round |
✓ | แบบทางอ้อม, ผ่าน qub_id พรีอิมเมจ |
body |
✓ | แบบทางอ้อม, ผ่าน body_hash = SHA3-256(body) |
author_pubkey |
— (โดยนัย) | กุญแจที่ยืนยันลายเซ็นคือผู้เขียน ตามคำนิยาม |
cosigner_pubkey / cosigner_signature |
— | ลงนามโดยอิสระในเอกสารเดียวกัน sig_input (ดู §9.7) |
drand_chain_id, tlock_ciphertext, visibility |
— | ด้านนอก SealedQub ฟิลด์ ไม่ใช่ภายในซองจดหมาย — ถูกครอบคลุมโดยค่าคงที่เชิงโครงสร้างของตัวเอง (ความสอดคล้องแบบรอบ / แบบโซ่) แต่ไม่ใช่โดยลายเซ็นของผู้เขียน. (drand_round ตอนนี้ถูกผูกผ่านทางอ้อมผ่าน qub_id preimage — ดูด้านบน.) |
เหตุใด V2 จึงเป็นพรีอิมเมจเดียวที่ยอมรับ
- ภายใต้พรีอิมเมจ V1 ที่ยกเลิกแล้ว ฝ่ายที่มีสิทธิ์เขียนไบต์ที่จัดเก็บไว้สามารถสลับ
sender_label("Alice" → "Mallory") หรือเปลี่ยนพ่อแม่ของreply_to— และเข้ารหัสใหม่หลังรอบ — โดยไม่ทำให้ลายเซ็นผู้เขียนไม่ถูกต้อง เพราะไม่มีฟิลด์ใดอยู่ในพรีอิมเมจที่ลงนาม V2 ครอบคลุมทั้งสองฟิลด์ ดังนั้นการเปลี่ยนแปลงฟิลด์ใดฟิลด์หนึ่งจะพลิกการตรวจสอบเป็น "ล้มเหลว" เนื่องจากตัวตรวจสอบยอมรับ V2 เท่านั้นในตอนนี้ การสลับนี้จึงถูกปิดสำหรับทุกลายเซ็น: ลายเซ็นที่ไม่ผูกฟิลด์ใดเลย (กล่าวคือ ตรวจสอบผ่านกับ V1 เท่านั้น) จะถูกปฏิเสธทันทีแทนที่จะถูกยอมรับด้วยการลดระดับลงมา author_pubkeyภายในซองยังคงเป็นจุดยึดเอกลักษณ์ที่แท้จริง — ผู้ชมต้องดึงเอกลักษณ์การแสดงผลจากauthor_pubkey(ผ่านชั้นการยืนยัน §9.5) แทนที่จะเชื่อsender_label
การใช้งานที่แสดง sender_label หรือ reply_to ให้ผู้ใช้ปลายทางต้องแสดงเอกลักษณ์ที่รับรองความถูกต้อง (ลายนิ้วมือกุญแจสาธารณะ การยืนยัน) เป็นสัญญาณเอกลักษณ์หลัก ไม่ใช่ป้าย
9.4 ขั้นตอนการตรวจสอบ
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."
การตรวจสอบลายเซ็นเป็นการดำเนินการที่มีค่าใช้จ่ายมากที่สุด (โดยเฉพาะ ML-DSA-65) ควรดำเนินการหลังจากที่การตรวจสอบที่ถูกกว่าทั้งหมด (แฮช, qub_id, unlock_at) ผ่านแล้ว
9.5 การยืนยันเอกลักษณ์
การยืนยันเอกลักษณ์ — การแมป author_pubkey กับการอ้างเอกลักษณ์ที่มนุษย์สามารถจดจำได้ เช่น qub handle ที่อยู่อีเมล social handle หรือข้อมูลรับรอง passkey — เป็น การปรับปรุงที่ก้าวหน้าฝั่งผู้ชม และ ไม่จำเป็น สำหรับการตรวจสอบลายเซ็น ผู้ชมที่แปลงการยืนยันเป็นเอกลักษณ์การแสดงผลต้องใช้ลำดับความสำคัญ:
handle > email > social > fingerprint
ค่าสำรองลายนิ้วมือคือเลขฐานสิบหกพิมพ์เล็กของ SHA3-256(author_pubkey); มีอยู่เสมอสำหรับ qub ที่ลงนามใด ๆ ผู้ชมอาจย่อค่านี้สำหรับการแสดงผล — ผู้ชมอ้างอิงเรนเดอร์ qub: ตามด้วยสี่ไบต์แรกและสี่ไบต์สุดท้าย (qub:<8 hex>…<8 hex>)
ตัวตรวจสอบที่สอดคล้องสามารถดำเนินการตรวจสอบทุกข้อใน §9.4 ได้อย่างสมบูรณ์โดยไม่ต้องติดต่อ API qub โดยไม่ต้องใช้เครือข่ายเกินกว่าพื้นที่เก็บถาวรและ drand และโดยไม่ต้องค้นหาฝั่งเซิร์ฟเวอร์ใด ๆ การแปลงการยืนยันเป็นขั้นตอนพยายามที่ดีที่สุดแยกต่างหากที่ดำเนินการเฉพาะหลังจากการตรวจสอบลายเซ็นสำเร็จแล้ว
9.6 ผลกระทบด้านขนาด
| Ed25519 | ML-DSA-65 | |
|---|---|---|
| ลายเซ็น | 64 ไบต์ | 3,309 ไบต์ |
| กุญแจสาธารณะ | 32 ไบต์ | 1,952 ไบต์ |
| รวมต่อ qub | 96 ไบต์ | 5,261 ไบต์ |
| ค่าใช้จ่ายการจัดเก็บเพิ่ม (ที่ ~$5/MB) | ~$0.0005 | ~$0.026 |
สำหรับ qub ข้อความขนาด 500–2,000 ไบต์ ML-DSA-65 ทำให้ขนาดที่จัดเก็บเพิ่มขึ้นประมาณสามเท่า ค่าใช้จ่ายสัมบูรณ์ไม่มีนัยสำคัญ
9.7 การตรวจสอบผู้ลงนามร่วม (ข้อตกลงทวิภาคีของสัญญา)
สำหรับข้อตกลงทวิภาคี (content_type = 0x03) ชั้นลายเซ็นที่สองพิสูจน์ว่าทั้งสองฝ่ายยินยอมตามเงื่อนไขเดียวกัน
ฟิลด์ซอง:
cosigner_pubkey: กุญแจสาธารณะ ML-DSA-65 ของผู้ลงนามคู่สัญญา (ฝ่าย B)cosigner_signature: ลายเซ็นบนsig_inputเดียวกันกับผู้เขียน (§9.3)
ทั้งสองฟิลด์ต้องอยู่ร่วมกันหรือไม่อยู่ร่วมกัน หากมีฟิลด์เดียวอยู่ ผู้ชมต้องรายงานข้อผิดพลาดความสมบูรณ์
ขั้นตอนการตรวจสอบ:
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."
คุณสมบัติ:
- ผู้ลงนามร่วมลงนาม
sig_inputที่เหมือนกันกับผู้เขียน — ทั้งสองฝ่ายผูกพันกับqub_id,body_hashและunlock_atเดียวกัน (และภายใต้ V2 คือsender_label_hashและreply_to_or_zeroเดียวกัน) - เพื่อให้ผู้ลงนามคู่สัญญาสามารถสร้างพรีอิมเมจ V2 ใหม่ได้โดยไม่ต้องเข้าถึงไบต์ซองดิบ บริการจัดเตรียมบังคับใช้ในเวลาจัดเตรียมว่า
sender_labelของซองสัญญาต้องเท่ากับpact_terms.party_a.labelและreply_toต้องไม่มีค่า ทั้งสองข้อเป็นจริงสำหรับสัญญาของไคลเอนต์อ้างอิงทุกฉบับ ซองที่ละเมิดจะถูกปฏิเสธในขั้นจัดเตรียม - การได้มาของ
qub_id(§4.1) ไม่รวมฟิลด์ผู้ลงนามร่วม การเพิ่มผู้ลงนามร่วมในซองที่มีอยู่ไม่เปลี่ยนqub_id - สัญญาสามารถลงนามโดยผู้เขียนเท่านั้น (ข้อผูกพันฝ่ายเดียว) ลงนามโดยผู้ลงนามร่วมเท่านั้น (ไม่ปกติ) หรือทั้งสอง (หลักฐานทวิภาคีเต็มรูปแบบ)
เกตการผูกอีเมล (การดำเนินงาน) เมื่อสัญญาที่จัดเตรียมไว้มีผู้ติดต่ออีเมลฝ่าย B (§6.1) บริการอัปโหลด qub ต้องปฏิเสธคำขอลงนามร่วมเว้นแต่จะมีเครื่องหมายยืนยันอีเมลอายุสั้นที่ตรงกับทั้ง staging id และแฮชอีเมลที่นอร์มัลไลซ์ของผู้ติดต่อนั้น เครื่องหมายนี้ถูกเขียนโดย /api/v1/auth/verify เมื่อโทเค็น magic-link มี staging_id และที่อยู่ที่ตรวจสอบแล้วตรงกับ SHA-256(normalise_email(party_b.contact)) — โดยที่ normalise_email(addr) รักษาตัวพิมพ์ของ local-part และทำให้ส่วนโดเมนเป็นตัวพิมพ์เล็กเท่านั้น (ตาม RFC 5321 §2.3.11) และ SHA-256 ที่นี่คือแฮช NIST FIPS 180-4 (แตกต่างจาก SHA3-256 ที่ใช้ในการหาค่า §4) — และหมดอายุ 900 วินาที (15 นาที) หลังจากออก นี่เป็นเกตป้องกันการปลอมตัวเชิงการดำเนินงาน ไม่ใช่ส่วนหนึ่งของหลักฐาน qub บนเชน — ตัวตรวจสอบของบุคคลที่สามที่เล่นซ้ำ §11 ต้องการเพียงพื้นที่เก็บถาวรและ drand โดยไม่ต้องค้นหาฝั่งเซิร์ฟเวอร์ใด ๆ เครื่องหมายมีอยู่เฉพาะฝั่งเซิร์ฟเวอร์เท่านั้นและไม่เคยเป็นส่วนหนึ่งของ body ที่ลงนาม
ผลกระทบด้านขนาด (ML-DSA-65 ผู้เขียน + ผู้ลงนามร่วม):
| องค์ประกอบ | ขนาด |
|---|---|
| ลายเซ็นผู้เขียน | 3,309 ไบต์ |
| กุญแจสาธารณะผู้เขียน | 1,952 ไบต์ |
| ลายเซ็นผู้ลงนามร่วม | 3,309 ไบต์ |
| กุญแจสาธารณะผู้ลงนามร่วม | 1,952 ไบต์ |
| ภาระการเข้ารหัสรวม | 10,522 ไบต์ |
| ค่าใช้จ่ายการจัดเก็บเพิ่ม | ~$0.05 |
10. การเรนเดอร์ Markdown และการทำความสะอาด
ส่วนนี้สำคัญต่อความปลอดภัย ผู้ชมเรนเดอร์ qub ข้อความ (content_type = 0x01) โดยใช้ชุดย่อย Markdown ที่จำกัด
10.1 องค์ประกอบที่อนุญาต
- หัวเรื่อง:
#ถึง####(ไม่มี#####หรือ######) - การเน้น: ตัวหนา (
**) ตัวเอียง (*) ขีดทับ (~~) - รายการ: เรียงลำดับ (
1.) และไม่เรียงลำดับ (-,*) - บล็อกอ้างอิง (
>) - โค้ด: ช่วงในบรรทัด (```) และบล็อกที่มีรั้ว (`````)
- เส้นแนวนอน (
---) - ตัวแบ่งบรรทัด (ช่องว่างปิดท้ายสองช่องหรือบรรทัดว่าง)
- ย่อหน้า
10.2 องค์ประกอบที่ห้าม
| องค์ประกอบ | การจัดการ |
|---|---|
HTML ดิบ (<div>, <script> เป็นต้น) |
ตัดออกทั้งหมด ไม่มี HTML ผ่านได้ |
รูปภาพ () |
ตัดออก ไวยากรณ์รูปภาพถูกลบออกจากเอาต์พุต |
ลิงก์ ([text](url)) |
URL เรนเดอร์เป็นข้อความธรรมดาที่มองเห็นได้ ไม่ลิงก์อัตโนมัติ คลิกไม่ได้โดยไม่มีการกระทำที่ชัดเจนของผู้ใช้ |
| รูปแบบ URL ที่อันตราย | javascript:, data:, vbscript:, file: — ตัดออก |
| Iframe, embed, object | ตัดออก |
| HTML entity | ถอดรหัสเป็นอักขระแสดงผลเฉพาะเมื่อปลอดภัย |
10.3 การใช้งาน
การใช้งานต้องใช้ ตัวแยกวิเคราะห์ allowlist ที่เข้มงวด ไม่ใช่ blocklist แนวทางที่แนะนำ:
- แยกวิเคราะห์ Markdown โดยใช้
pulldown-cmark(หรือเทียบเท่า) - เดิน AST และตัดโหนดใด ๆ ที่ไม่อยู่ใน allowlist (§10.1)
- สำหรับโหนดลิงก์: ส่ง URL เป็นข้อความที่มองเห็นได้ ไม่ใช่เป็นองค์ประกอบ
<a>ที่คลิกได้ - แปลง AST ที่กรองแล้วเป็น การแทนกลางที่กำหนดประเภท (เช่น enum
MarkdownNodeที่มีเฉพาะตัวแปรที่ปลอดภัย) HTML ดิบไม่สามารถแทนได้ในเชิงโครงสร้างใน IR นี้ - เรนเดอร์จาก IR ที่กำหนดประเภทไปยังชั้นมุมมองเป้าหมาย (เช่น คอมโพเนนต์มุมมองแบบรีแอกทีฟ โหนด DOM) ไม่มีการต่อสตริง HTML หรือ
innerHTMLในจุดใดก็ตาม
แนวทาง blocklist เปราะบางเนื่องจากส่วนขยาย Markdown ใหม่หรือลักษณะเฉพาะของตัวแยกวิเคราะห์อาจแนะนำองค์ประกอบที่ไม่ได้กรอง แนวทาง AST ที่กำหนดประเภททำให้ XSS เป็นไปไม่ได้ในเชิงโครงสร้าง — ไม่มีตัวแปรใดที่สามารถถือ HTML ตามอำเภอใจได้
10.4 ข้อจำกัดด้านขนาดและโครงสร้าง
- ความลึกหัวเรื่องที่เรนเดอร์สูงสุด:
####(H4)#####และลึกกว่าถูกเรนเดอร์เป็นข้อความตัวหนา - ไม่มีข้อจำกัดในจำนวนย่อหน้า (ข้อจำกัดขนาด body ใน §6 เป็นข้อจำกัด)
- บล็อกโค้ดที่มีรั้ว: ไม่มีการเน้นไวยากรณ์ใน MVP เรนเดอร์เป็นข้อความที่จัดรูปแบบไว้ล่วงหน้าแบบ monospace
11. การตรวจสอบโดยบุคคลที่สาม
บุคคลภายนอกใด ๆ ที่ถือไบต์ที่จัดเก็บไว้ (และ K สำหรับ qub ส่วนตัว/ห่อ) สามารถ ตรวจสอบสิ่งประดิษฐ์ทางคริปโตกราฟฟิกโดยไม่ต้องมีความร่วมมือของคิวบ์ อย่างอิสระ ติดเวลาหมายเหตุ การมีอยู่ คำเรียกร้องเพิ่มเติมต้องการทั้งยืนยันแล้ว การรวมการจัดเก็บแบบถาวร per-qub หรือหลักฐานการตรวจสอบความโปร่งใส §16 ที่ได้รับการยืนยัน
1. Obtain the stored bytes. For a private delivery, also obtain K from the
delivery-link fragment.
2. Resolve the delivery shape (§8 step 3a): unwrap OuterWrapper with K, or
accept bare SealedQubCbor. Require shape/visibility agreement.
3. Parse SealedQubCbor → SealedQub; validate protocol version, visibility,
content type, chain identity, and structural bounds.
4. Recompute expected_round from unlock_at (§4.3); require the stored round
(allowing the documented legacy minus-one case) and ciphertext-stanza round
to agree exactly as §8 step 6a specifies.
5. Obtain the drand signature for SealedQub.drand_round and verify its BLS
signature against the pinned chain public key.
6. tlock_decrypt(tlock_ciphertext, round_signature) → QubEnvelope CBOR bytes.
7. Parse → QubEnvelope.
8. Verify SHA3-256(body) == body_hash.
9. Verify envelope/sealed equality for qub_id, unlock_at, and outcome_at.
10. Recompute qub_id from the decrypted fields, sealed drand_round, and title;
require it to equal the carried qub_id (§4.1).
11. If sig_alg != 0x00, verify the V2 author signature (§9.4). If cosigner
fields are present, verify their pairing, key separation, and signature
(§9.7).
12. For an existence-time claim, independently verify either:
a. the permanent-storage transaction's data-to-id binding, owner, block
inclusion, and block timestamp; or
b. a §16 inclusion proof through its pinned anchor and anchor block time.
13. Report each verified claim separately; do not collapse an absent storage
or anchor proof into a successful timing verdict.
การยืนยันพิสูจน์ว่า:
| ป้อนหลักฐาน | สิ่งที่มันกำหนด |
|---|---|
| ชุดถูกต้อง / สิ่งประดิษฐ์ปิดผนึก + ลายเซ็น drand | ศพที่เก็บคืนตรงกับ body_hash; เมทาดาต้าที่ถูกผูกเข้ากับ qub_id ยังสมบูรณ์; ข้อความที่เข้ารหัสถูกผูกกับรอบ drand ที่ประกาศไว้; และรอบนั้นได้ผ่านไปแล้ว สิ่งนี้ทำ ไม่ กำหนดเวลาที่ข้อความเข้ารหัสถูกสร้างขึ้น |
| ลายเซ็นผู้เขียน/ผู้ร่วมลงนาม V2 ที่ถูกต้อง | ผู้ถือกุญแจลับที่สอดคล้องกันได้ทำการยืนยันตัวตนของพื้นผิวที่ลงนามใน §9.3 |
| ธุรกรรมการจัดเก็บต่อคิวบ์ที่ได้รับการตรวจสอบอย่างอิสระ | ข้อความเข้ารหัสที่เก็บไว้อย่างแน่นอนมีอยู่ไม่เกินเวลาตราประทับของบล็อกนั้น |
| หลักฐานบันทึกความโปร่งใสที่ยึดกับสมอที่ถูกต้อง | ข้อเรียกร้องเฉพาะชนิดของใบใน §16.11 รวมถึงเวลาผูกมัดขอบเขตบนจากบล็อกสมอ |
สิ่งที่การตรวจสอบไม่ได้พิสูจน์:
| ไม่พิสูจน์ | ทำไม |
|---|---|
| ผู้เขียน | นั้น sender_label เป็นของตกแต่ง โดยไม่มี sig_alg ≥ 0x01, ใครๆ ก็สามารถปิดผนึกเนื้อหานี้ได้ |
| ความตั้งใจ | สิ่งประดิษฐ์นี้พิสูจน์บิตและความสัมพันธ์ทางคริปโตกราฟิก ไม่ใช่สิ่งที่ผู้สร้างตั้งใจหมายถึงโดยนัยส่วนตัว |
ข้อผูกพันที่มีอยู่ก่อนจาก .qub คนเดียว |
ผู้สร้างสามารถรวบรวมชุดที่ถูกต้องได้หลังจากรอบที่กำหนดสิ้นสุดลง ลายเซ็น drand ที่ฝังอยู่พิสูจน์ว่ารอบได้สิ้นสุดลงแล้ว ไม่ได้พิสูจน์ว่าข้อความเข้ารหัสมีอยู่ก่อนหน้านั้น |
| เวลาปุ่มประทับตราที่แน่นอน | เวลาบันทึกของบล็อกจัดเก็บหรือบล็อกยึดเป็นขอบเขตบนที่สามารถตรวจสอบได้อย่างอิสระ และอาจล่าหลังการกระทำของผู้ใช้ในพื้นที่ของตน sealed_at / received_at ข้ออ้างเหล่านี้ไม่มีหลักฐานสนับสนุน |
บันทึกความโปร่งใสที่นำไปใช้ (§16) ขยายการตรวจสอบข้ามควับส์ด้วย
ปลอมแปลงได้ยาก การสั่งซื้อ และไม่ต้องมีความไว้วางใจ เวลาผูกมัดสูงสุด (the
เวลาโครงบล็อกแองเคอร์), จำกัดขอบเขตตามประเภทของใบ (§16.11). มันไม่เพิ่มการเป็นผู้แต่งหรือ
เจตนา; สำหรับเส้นทางการอัปโหลดแบบไม่ตรวจไบต์เริ่มต้น มันเองไม่ได้พิสูจน์
body_hash หรือ drand_roundซึ่งยังคงมาจากการตรวจสอบสิ่งประดิษฐ์
12. การควบคุมเวอร์ชันและการปล่อย
การเผยแพร่เอกสาร โปรโตคอลสายภายใน และตัวหุ้มภายนอกเป็นสิ่งแยกจากกัน ช่องว่างของเวอร์ชัน ดังนั้นเอกสารเพียงอย่างเดียวไม่สามารถชี้แจงอย่างเงียบๆ เปลี่ยนไบต์ และการย้ายสายเคเบิลในอนาคตไม่สามารถปลอมตัวเป็นงานบรรณาธิการได้ การแก้ไข
12.1 เวอร์ชันเอกสารที่เผยแพร่
ข้อกำหนดนี้ใช้การเผยแพร่เอกสารเชิงความหมาย (MAJOR.MINOR.PATCH) และ
แท็ก Git ที่ไม่สามารถเปลี่ยนแปลงได้ชื่อว่า protocol-v<release>.
- แพตช์: ความถูกต้องหรือการแก้ไขเชิงบรรณาธิการที่ไม่เปลี่ยนไบต์ที่สอดคล้องกันหรือพฤติกรรมที่จำเป็น
- เล็กน้อย: การเพิ่มเติมมาตรฐานที่เข้ากันได้ย้อนหลัง รายการทะเบียนใหม่ หรือรูปแบบไซด์คาร์เวอร์ชันอิสระใหม่
- ใหญ่: การเปลี่ยนแปลงทางบรรทัดฐานที่ไม่เข้ากัน รวมถึงการตีความสายไฟใหม่ที่จำเป็น
สถานะการปล่อยเป็นหนึ่งใน ร่าง (ยังไม่เป็นเกณฑ์มาตรฐาน) ปัจจุบัน (เพียงผู้เดียว
เป้าหมายการนำไปใช้ที่แนะนำ) หรือ ถูกแทนที่ (เก็บไว้เพื่อวัตถุประสงค์ทางประวัติศาสตร์
การตรวจสอบ) ที่ไม่ได้ระบุรุ่น /protocol เส้นทางแสดงการปล่อยรุ่นปัจจุบัน;
แท็กการปล่อยจะรักษาต้นฉบับของมันไว้ทุกประการและทุกภาษาท้องถิ่นที่เผยแพร่พร้อมกับมัน
การเปลี่ยนสถานะหรือหมายเลขรุ่นจำเป็นต้องอัปเดตตารางนี้และการปล่อย
ประวัติในการเปลี่ยนแปลงที่ตรวจสอบเหมือนกัน
| การปล่อยเอกสาร | วันที่มีผลบังคับใช้ | สถานะ | โพรโตคอลสายไฟ | ห่อ | แหล่งที่มา |
|---|---|---|---|---|---|
| 1.0.0 | 23-09-2026 | ปัจจุบัน | 0x01 |
0x01 |
protocol-v1.0.0 |
12.2 เวอร์ชันโปรโตคอล
นั้น version ฟิลด์ (u8) ทั้งสอง SealedQub และ QubEnvelope ระบุเวอร์ชันโปรโตคอลหลัก
- ผู้ชมต้องปฏิเสธเวอร์ชันหลักที่ไม่ทราบด้วยข้อผิดพลาดที่ชัดเจน
- ภายในเวอร์ชันหลักที่ทราบแล้ว ตัวถอดรหัสต้องปฏิเสธคีย์แผนที่ที่ไม่รู้จัก (§3.1) — การพัฒนาโครงร่างเกิดขึ้นโดยการแนะนำใหม่
version, ไม่ใช่โดยการเพิ่มคีย์ที่ตัวถอดรหัสที่มีอยู่จะข้ามไป (การแก้ไขก่อนหน้านี้ของข้อกำหนดนี้อนุญาตให้สามารถทนต่อฟิลด์ตัวเลือกที่ไม่รู้จัก; ข้อกำหนดนั้นถูกถอน — มันทำให้encode(decode(x))ไม่เป็นฟังก์ชันหนึ่งต่อหนึ่งและเปิดเวกเตอร์เนื้อหาที่ลงชื่อซ่อนอยู่บนพาหนะของพันธะ.) - ประเภทเนื้อหา (
content_type) และโครงการลายเซ็น (sig_alg) จะถูกจำกัดตามเวอร์ชัน: ค่าที่ใหม่อาจจะถูกแนะนำได้เฉพาะเมื่อมีเวอร์ชันโปรโตคอลใหม่หรือการอัปเดตรีจิสทรีอย่างชัดเจนเท่านั้น
12.3 ประวัติรุ่นโปรโตคอล
| รุ่น | ค่า | คำอธิบาย |
|---|---|---|
| เวอร์ชัน 1 | 0x01 |
การจัดส่งแบบส่วนตัว/ห่อและแบบสาธารณะ/เปลือย; ข้อความ (0x01), ข้อตกลง (0x03), และคำตัดสิน (0x04) ร่างกาย; ผู้เขียน/ผู้ร่วมลงนาม ML-DSA-65 V2 ลงนาม; drand quicknet tlock; SHA3-256. |
12.4 ความเข้ากันได้ด้านหน้า
ตัวผู้ชม v1 ที่พบ QubEnvelope ด้วยคีย์แผนที่ CBOR ที่ไม่รู้จัก (คีย์ที่ไม่ได้อยู่ในลำดับมาตรฐาน §3.2) ต้องปฏิเสธมันพร้อมกับข้อผิดพลาดในการถอดรหัส (§3.1). ความเข้ากันได้ในอนาคตขึ้นอยู่กับ version ฟิลด์ ไม่ใช่ด้านความทนทานของคีย์: การเพิ่มในอนาคต — แม้กระทั่งข้อมูลเล็กน้อย — จะถูกส่งภายใต้ใหม่ version ค่าซึ่งผู้ชม v1 ปฏิเสธด้วยข้อผิดพลาด “โปรโตคอลรุ่นใหม่” อย่างชัดเจน แทนที่จะปล่อยเนื้อหาที่ลายเซ็นยืนยันไว้โดยไม่แจ้งเตือน
ผู้ชม v1 ที่พบเจอ sig_alg = 0x01 (ML-DSA-65) แต่หากขาดการรองรับการตรวจสอบ ML-DSA-65 ควรแสดงเนื้อหา qub พร้อมข้อความ “มีลายเซ็นต์แต่ไม่สามารถยืนยันได้” ไม่ควรปฏิเสธ qub ทั้งหมด การใช้งานอ้างอิงในปัจจุบันปฏิเสธทุก sig_alg ค่าอื่นที่ไม่ใช่ 0x00 และ 0x01 เนื่องจากทะเบียน v1 ไม่มีอัลกอริธึมที่ถูกต้องอื่น — การปฏิเสธอย่างเข้มงวดและการล้มเหลวแบบอ่อนจะเหมือนกันตามการสังเกตจนกว่าจะมีการลงทะเบียนอัลกอริธึมที่สาม พฤติกรรมการล้มเหลวแบบอ่อนด้านบนจะกลายเป็นที่รองรับเมื่อ §9.2 ยอมรับรายการใหม่ และตัวดูอ้างอิงจะได้รับการอัปเดตเป็นล้มเหลวแบบอ่อนในเวลานั้น
12.5 เวอร์ชันตัวห่อภายนอก
OuterWrapper ที่อธิบายใน §13 มีของมันเอง version ไบต์, อิสระ ของ SealedQub.version และ QubEnvelope.version. สองเวอร์ชันสเปซพัฒนาต่างหาก: การแทนที่สมมาตรที่ปลอดภัยสำหรับอนาคตหลังคอนเทียมจะปรับไบต์ของตัวห่อโดยไม่แตะต้องเวอร์ชันโปรโตคอลภายใน และการเพิ่มชั้นโปรโตคอลในอนาคต (เช่น ฟิลด์ซองใหม่) จะปรับเวอร์ชันภายในโดยไม่แตะต้องไบต์ของตัวห่อ
OUTER_WRAPPER_VERSION_* |
ค่า | อัลกอริทึม | สถานะ |
|---|---|---|---|
OUTER_WRAPPER_VERSION_1 |
0x01 |
AES-256-GCM พร้อม nonce ขนาด 12 ไบต์, แท็กการตรวจสอบความถูกต้องขนาด 16 ไบต์, AAD ผูกกับ qub_id |
เปิดใช้งานสำหรับการจัดส่งส่วนตัว |
| — | 0x02–0xFF |
สงวน | อนาคต |
ผู้ชมต้องปฏิเสธเวอร์ชัน wrapper ที่ไม่รู้จักด้วยข้อผิดพลาดที่ชัดเจน โปรโตคอลตั้งใจที่จะรักษาช่องเวอร์ชันของ wrapper ให้แคบจนกว่าจะมีไดร์เวอร์การย้ายข้อมูลที่ชัดเจนปรากฏ (เช่น คำแนะนำของ NIST ที่สนับสนุน AEAD ที่แตกต่าง); a 0x02 ช่องจะถูกจัดสรรในฉบับแก้ไขเดียวกันที่แนะนำอัลกอริธึม
13. ตัวห่อหุ้มการเข้ารหัสภายนอก
13.1 เหตุผล
ชั้นโปรโตคอล (QubEnvelope → tlock → SealedQub) ทำให้ qub ที่ผนึกแล้ว ล็อกเวลา: body ไม่สามารถอ่านได้จนถึง unlock_at และลายเซ็นรอบ drand ได้รับการเผยแพร่ อย่างไรก็ตามหลังจากปลดล็อก ลายเซ็นรอบเป็นสาธารณะและรูปร่าง CBOR มาตรฐานของ SealedQub สามารถจดจำได้ ดังนั้นผู้เก็บเกี่ยวที่จัดทำดัชนีธุรกรรมพื้นที่เก็บถาวรจึงสามารถถอดรหัสคลังข้อมูล qub ทั้งหมดเป็นจำนวนมากได้
สำหรับการส่งมอบแบบส่วนตัว ชั้นห่อเข้ารหัสภายนอกจะปิดช่องทางนั้นโดยการแทรกชั้น AEAD สมมาตรเพิ่มเติมระหว่างแบบมาตรฐาน SealedQubCbor และไบต์ที่เก็บ ในเส้นทาง browser-seal กุญแจ 256 บิต K ชีวิต เท่านั้น ในส่วนของ URL fragment ของ URL การจัดส่งและบนอุปกรณ์ของผู้ใช้; เบราว์เซอร์จะไม่ส่ง URL fragment ไปยังเซิร์ฟเวอร์ ดังนั้น qub.social เกตเวย์จัดเก็บทุกแห่ง และ CDN ทุกแห่งที่อยู่ด้านหน้าของทั้งสองจะไม่สามารถสังเกตเห็นได้ K. การเก็บข้อมูลของ qub ส่วนตัวจึงเป็นรหัสที่ไม่สามารถเข้าใจได้โดยตรงซึ่งข้อความต้นฉบับไม่สามารถกู้คืนได้หากไม่มี URL ที่ผู้สร้างเลือกจะแชร์ การส่งมอบต่อสาธารณะได้ละเว้นขั้นตอนนี้โดยเจตนา (§13.8)
ผลสุทธิ:
- ความต้านทานการนับสำหรับการจัดส่งส่วนตัว
OuterWrapperยังคงสามารถจดจำได้ว่าเป็น CBOR ที่มีโครงสร้าง—มันไม่ได้เหมือนกับไบต์สุ่มโดยตรง—แต่ช่องรหัสลับของมันซ่อนส่วนภายในที่สามารถจดจำได้SealedQubรูปร่าง กลยุทธ์การเก็บข้อมูลที่บันทึกไว้ของ "GraphQL-query สำหรับการอัปโหลดรูปทรง qub เปล่า, การถอดรหัสจำนวนมากด้วยลายเซ็น drand สาธารณะ" จะไม่สิ้นสุดด้วยข้อความธรรมดาหากไม่มี K - ท่าทางความเป็นส่วนตัวแบบเข้ารหัสสำหรับกระบวนการเบราว์เซอร์ส่วนตัวเริ่มต้น qub.social ไม่สามารถถอดรหัสสิ่งประดิษฐ์ที่เก็บไว้จากข้อมูลฝั่งเซิร์ฟเวอร์เริ่มต้น การกู้คืนโดยชัดแจ้ง การส่งมอบสาธารณะ และการปิดผนึกฝั่งเซิร์ฟเวอร์ที่เชื่อถือได้มีขอบเขตความน่าเชื่อถือที่เปิดเผยต่างกัน
- บันไดความลับสองชั้น ค่าเริ่มต้น = การเข้าถึงควบคุมด้วยลิงก์ (ส่วนนี้) คิวบส์ส่วนตัวที่เข้ารหัสโดยผู้รับ (คุณสมบัติ Phase-2 ที่สงวนไว้ ยังไม่ได้ระบุ) จะซ้อนอยู่ด้านบนเป็นชั้นที่สอง
13.2 การจัดชั้น
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
├─ public (visibility=0x01) ───────────────▶ stored directly (§13.8)
└─ private (visibility=0x00)
↓ AES-256-GCM(K, nonce, AAD=qub_id) (§7 step 12, this section)
OuterWrapper CBOR bytes ← stored private payload
การผนึกและปลดล็อกที่ชั้นโปรโตคอล (§7, §8) ไม่เปลี่ยนแปลงด้านล่างขอบเขตของตัวห่อหุ้ม; ตัวห่อหุ้มแนบที่จุดเรียกของ seal() และถอดออกที่จุดเรียกของ unlock()
13.3 โครงสร้างข้อมูล OuterWrapper
struct OuterWrapper {
version: u8, // 0x01, see §12.5
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
}
ค่าคงที่ของฟิลด์
versionต้องเท่ากับ0x01สำหรับไบต์ของตัวห่อ v1.0qub_idต้องเท่ากับqub_idสนามของ SealedQub ที่กู้คืนหลังจากแกะ ทั้งสองอ้างอิงwrap_sealed_qubและunwrap_sealed_qubวิเคราะห์ CBOR ภายในและบังคับความเท่าเทียมนี้โดยตรง; การผูก AAD แยกต่างหากทำให้การปลอมแปลงหลังการห่อหุ้มกับภายนอกqub_idการตรวจสอบสิทธิ์ล้มเหลวnonceต้องมีความยาว 96 บิต (12 ไบต์) สร้างใหม่โดย CSPRNG สำหรับการทำงานห่อแต่ละครั้ง การใช้ nonce ซ้ำภายใต้กุญแจเดียวกันทำให้เกิดการโจมตี AEAD nonce-reuse ที่สามารถกู้คืนข้อความต้นฉบับได้; ผู้ผลิตต้องปฏิบัติ (key,nonce) คู่เป็นแบบครั้งเดียวciphertextเอาต์พุตของ AES-256-GCM คือ ไบต์ของข้อความที่เข้ารหัสที่ต่อรวมกับแท็กการตรวจสอบความถูกต้องขนาด 16 ไบต์ciphertext.len() == SealedQubCbor.len() + 16ถูกต้อง
การเข้ารหัส CBOR CBOR มาตรฐานตาม §3 ด้วยกฎการเรียงลำดับกุญแจเดียวกัน (เรียงตามความยาวไบต์ที่เข้ารหัสจากน้อยไปมาก แล้วเรียงตามพจนานุกรม) กุญแจทั้งสี่คือ:
| กุญแจ | ไบต์ที่เข้ารหัส | ลำดับ |
|---|---|---|
nonce |
6 | 1 |
qub_id |
7 | 2 |
version |
8 | 3 |
ciphertext |
11 | 4 |
ดังนั้นไบต์แรกของ CBOR OuterWrapper คือเฮดเดอร์ map ความยาวกำหนดได้สำหรับ map 4 รายการ (0xA4)
13.4 การผูก AAD เข้ากับ qub_id
ตัวห่อหุ้มผูก qub_id เป็นข้อมูลรับรองความถูกต้องเพิ่มเติมของ AEAD นี่คือการป้องกันเชิงโครงสร้างที่สำคัญต่อการโจมตีสามประเภท:
| โจมตี | การป้องกัน |
|---|---|
ย้ายข้อความเข้ารหัสไปยังที่ต่างกัน qub_id ฟิลด์ในตัวห่อ |
AAD ไม่ตรงกัน → การตรวจสอบความถูกต้องของ AEAD ล้มเหลว |
| ผสมชิ้นส่วน URL ของ qub A กับไบต์ที่เก็บไว้ของ qub B | กุญแจผิด (และ AAD ที่ผูกแยกต่างหาก) → การตรวจสอบความถูกต้องของ AEAD ล้มเหลว |
ดัดแปลงกับ qub_id ช่องของตัวห่อหลังจากอัปโหลด |
AAD ไม่ตรงกัน → การตรวจสอบความถูกต้องของ AEAD ล้มเหลว |
การพก qub_id ในข้อความธรรมดาของตัวห่อหุ้มไม่ลดภูมิคุ้มกันการระบุอย่างมีนัยสำคัญ — qub_id เองเป็นแฮช SHA3-256 ของพรีอิมเมจ §4.1 โดยไม่มีพรีอิมเมจที่สามารถกู้คืนจากไดเจสต์ได้ และผู้ระบุที่เก็บเกี่ยวไบต์ตัวห่อหุ้มไว้แล้วจะไม่เรียนรู้อะไรจาก qub_id ที่มองเห็นได้ที่พวกเขาไม่สามารถอนุมานได้จากการมีอยู่ของการอัปโหลดเอง
13.5 อัลกอริทึมห่อและแกะ
wrap_sealed_qub(SealedQubCbor S, qub_id Q, key K, nonce N):
require K.len() == 32 and N.len() == 12 and Q.len() == 32
I := canonical_cbor_decode(S) as SealedQub
require I.qub_id == Q // reject mismatched caller AAD
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.5
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
S := canonical_cbor_decode(P) as SealedQub
require S.qub_id == O.qub_id // explicit inner/outer cross-check
return P // P is the validated SealedQubCbor
การยุบโหมดล้มเหลว K ผิด nonce ผิด AAD ไม่ตรงกัน และไซเฟอร์เท็กซ์ที่ถูกดัดแปลง ทั้งหมดสร้างข้อผิดพลาด DECRYPT_FAILED เดียวกัน นี่เป็นคุณสมบัติ AEAD โดยเจตนา: การแยกแยะโหมดล้มเหลวจะสร้างช่องด้านข้างที่ผู้โจมตีระยะไกลสามารถสำรวจได้โดยส่งตัวห่อหุ้มที่ผิดรูปแบบและจับเวลาการตอบสนอง การใช้งานอ้างอิงต้องยุบความล้มเหลว AEAD ทั้งหมดเป็นรูปแบบข้อผิดพลาดเดียว
13.6 วัสดุกุญแจและการแจกจ่าย
กุญแจห่อหุ้ม K เป็นค่าสุ่มสม่ำเสมอ 256 บิตที่สร้างต่อ qub โดย CSPRNG การใช้งานอ้างอิงดึงจาก:
- ผู้สร้าง WASM:
getrandom(WebCrypto ภายใต้แบ็กเอนด์wasm_js) - ผู้เรียก API ผนึกฝั่งเซิร์ฟเวอร์: CSPRNG ในเครื่องของตน โดยผู้เรียกเป็นผู้จัดหาและเก็บรักษา
Kในรูปwrapper_key_b64urlWorker ใช้Kในหน่วยความจำสำหรับตัวห่อหุ้มแต่ต้องไม่คงเก็บไว้ สิ่งนี้ทำให้การลองซ้ำแบบ idempotent สามารถกู้คืนการตอบสนองที่ถูกกลบข้อมูลได้โดยใช้ความสามารถที่ผู้เรียกเก็บรักษาไว้ แทนที่จะพึ่งพาความลับที่เซิร์ฟเวอร์สร้างแบบยิงครั้งเดียว
การแจกจ่าย: K ต้องถูกเข้ารหัสเป็น base64 ที่ปลอดภัยสำหรับ URL (RFC 4648 §5 ไม่มี padding) และต่อท้ายลิงก์ส่งเป็นองค์ประกอบ fragment:
delivery_url = <origin>/c/<arweave_tx_id>#<base64url(K)>
Fragment ไม่ถูกส่งไปยังเซิร์ฟเวอร์ใด ๆ โดยเบราว์เซอร์ที่สอดคล้อง ช่องทางการกู้คืน (ดัชนีประวัติฝั่งเซิร์ฟเวอร์ การส่งอีเมลอัตโนมัติแบบเลือกได้) ที่คงลิงก์ส่งทั้งหมด — รวมถึง fragment — เกินกว่าอุปกรณ์ของผู้ใช้เป็นการแลกเปลี่ยนที่ชัดเจนกับท่าทีทำลายการเข้ารหัสเริ่มต้นและต้องมีการยินยอมที่ชัดเจนของผู้ใช้
การสูญหายของ Fragment หากผู้ใช้สูญเสีย URL fragment และไม่มีช่องทางการกู้คืน qub ไม่สามารถอ่านได้ นี่คือการแลกเปลี่ยนที่สำคัญของการออกแบบและต้องเปิดเผยให้ผู้ใช้ทราบในเวลาผนึก MVP เสริมการเปิดเผยในเวลาผนึกด้วยข้อความ "บันทึก URL นี้" ที่ชัดเจนและช่องทางการกู้คืนอีเมลที่ตรวจสอบแล้วสำหรับผู้ใช้ที่เลือกรับ
13.7 นอกขอบเขตของส่วนนี้
- การลงชื่อผู้แต่ง (§9) ไม่เปลี่ยนแปลง: ลายเซ็นถูกคำนวณภายในส่วนใน
QubEnvelopeและถูกกู้คืนหลังจากแกะห่อ → ถอดรหัส tlock → วิเคราะห์ CBOR. - การเข้ารหัสกุญแจสาธารณะของผู้รับ (ที่สงวนไว้
recipient_pubkeyสนาม) เป็นฟีเจอร์ในอนาคตที่แตกต่างจากโหมดห่อหุ้มที่มีความสามารถในการเชื่อมโยงและเป็นส่วนตัวในปัจจุบัน - กระแสการลงนามร่วมของเซิร์ฟเวอร์ปัจจุบันส่งออกสาธารณะ/เปลือย
SealedQubCborด้วยความชัดเจน0x01; มันไม่สามารถสนองต่อโมเดลความลับ K เฉพาะเบราว์เซอร์ได้ เพราะการปิดผนึกสุดท้ายเกิดขึ้นหลังจากการลงนามร่วมผ่านเซิร์ฟเวอร์ ผู้ผลิตสัญญาส่วนตัวในอนาคตอาจใช้ตัวห่อเดียวกัน ซึ่งไม่สามารถเห็นประเภทเนื้อหาภายในได้
13.8 qub สาธารณะ (การละเว้นตัวห่อหุ้ม)
ชั้นห่อด้านนอกคือ ไม่บังคับที่ชั้นการส่งมอบ. ผู้สร้างอาจปิดผนึกคิวบ์เป็น สาธารณะ, ในกรณีที่เป็น canonical SealedQubCbor เข้าสู่ท่อเก็บข้อมูล โดยตรง, โดยไม่มี OuterWrapper ชั้นและไม่มีปุ่ม K:
SealedQubCbor bytes ──(public)──▶ stored as-is
SealedQubCbor bytes ──(private)─▶ AES-256-GCM(K, …) ▶ OuterWrapper ▶ stored
ผับสาธารณะคือ ล็อกเวลาแต่ไม่ล็อกลิงก์: มันยังคงอ่านไม่ได้จนกว่ารอบการแจกจ่ายแบบสุ่มของมันจะเผยแพร่ (ชั้น tlock ไม่ได้เปลี่ยนแปลง) แต่หลังจากปลดล็อก ใครก็ตามที่มีรหัสธุรกรรมการจัดเก็บข้อมูลสามารถถอดรหัสได้ — ไม่จำเป็นต้องมีส่วนของ URL เพราะไม่มี K. นี่คือการแลกเปลี่ยนที่ตั้งใจสำหรับพื้นผิวที่เซิร์ฟเวอร์ต้องขับเคลื่อน: อีเมลแจ้งเตือนการเปิดเผย, ลิงก์ oEmbed/อัตโนมัติที่ไม่มีส่วนย่อย, และ SEO หลังการเปิดเผยที่สมบูรณ์ยิ่งขึ้น ทั้งหมดต้องการลิงก์ที่ใช้งานได้โดยไม่ต้องใช้ความลับที่เซิร์ฟเวอร์ไม่เคยถือ (§13.6). qub ส่วนตัวยังสามารถใช้ explicit <qub-embed src="full_delivery_url"> ฟอร์มเมื่อผู้จัดพิมพ์จัดหา ความสามารถในการรองรับชิ้นส่วนครบถ้วน
ผลที่ผู้ผลิตต้องคำนึงถึง:
- ไม่มีภูมิคุ้มกันการนับจำนวน Qub สาธารณะสละคุณสมบัติความคุ้มกันการระบุ §13.1 โดยการสร้าง บริการอัปโหลดอ้างอิงประทับตรา
Visibility: publicแท็กการเก็บถาวรบนพวกมัน (และเฉพาะพวกมัน) ดังนั้นพวกมันจึงสามารถถูกค้นพบได้โดยตั้งใจ; qubs ส่วนตัวไม่มีแท็กดังกล่าวและรักษาความไม่สามารถแยกแยะไบต์ของพวกมันไว้ - ชื่อเรื่องแบบข้อความธรรมดาถูกเปิดเผยในเวลาประทับตรา มาตรา §3.2
titleช่องข้อมูลเป็นข้อความธรรมดาภายในSealedQubCbor. ภายใต้ห่อ มันถูกซ่อนไว้จนกว่าผู้ชมจะจัดหาK; หากไม่มีตัวห่อ มันจะสามารถอ่านได้ทั่วโลกบนที่เก็บถาวร ตั้งแต่ช่วงเวลาที่อัปโหลด, ก่อนปลดล็อก แอปของผู้สร้างที่เป็นไปตามข้อกำหนดต้องเปิดเผยสิ่งนี้ในเวลาประทับตรา - การตรวจจับเป็นเชิงโครงสร้างและมีการตรวจสอบข้ามด้าน ตัวดู/ตัวฝังที่เป็นไปตามมาตรฐานจะแยกรูปร่างที่เก็บไว้ทั้งสองโดยการวิเคราะห์: ไบต์ที่สามารถวิเคราะห์ได้เป็น
OuterWrapperนำไปแกะด้วย-Kเส้นทาง; ไบต์ที่แยกวิเคราะห์เป็นแบบเปล่าSealedQubCborได้รับการยอมรับโดยตรง ค่าภายในที่กู้คืนต้องตรงกัน (0x00สำหรับห่อ/ส่วนตัว0x01สำหรับเปลือย/สาธารณะ).qub_idไม่ได้ผูกมัดความชัดเจน แต่ตามมาตรฐานSealedQubไบต์มีการส่งผ่าน ดังนั้นการเข้ารหัสภายในสาธารณะและส่วนตัวจึงไม่เหมือนกันในระดับไบต์
ส่วนตัว (ห่อหุ้ม) ยังคงเป็นค่าเริ่มต้น; สาธารณะเป็นทางเลือกของผู้สร้างต่อ qub อย่างชัดเจน
14. เวกเตอร์ทดสอบ
14.1 การหาค่า 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 = 4695446 (= floor((1736294400 - 1595431050) / 30) + 1, §4.3 mapping, 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—current protocol v1):
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)
0x000000000047A596 || // drand_round as u64 big-endian (4695446)
body_hash || // 32 bytes
title_hash // 32 bytes (all-zeros sentinel; title absent)
Expected output:
qub_id = SHA3-256(preimage)
= 4a84e3dfaec32954949c30073f8e6506
fd3204c1bb97f9162b81c7587afe412e
การดำเนินการต้องให้ผลลัพธ์เหมือนกัน body_hash และ qub_id ค่าต่าง ๆ สำหรับข้อมูลนำเข้าชุดนี้ เวกเตอร์ทดสอบนี้ ควรเป็นยูนิตเทสต์แรกที่ถูกเขียน ค่ามาตรฐานข้างต้นถูกคำนวณโดยการใช้งานอ้างอิงและต้องตรงกันทุกบิต รูปแบบต้นแบบก่อนการเปิดตัวทางประวัติศาสตร์ (ไม่มีคิวบ์สดใดขึ้นอยู่กับสองตัวแรก) ใช้ 92 ไบต์ก่อน outcome_at (3d9fc2390eab043d38a1669ed3b71be76f9eefe872b9569ab1aaa027b88392b0) และ 100 ไบต์หลังจากการเพิ่ม outcome_at_or_zero (b0d032898ad629795150fdcb3f84e518f59ed05b7a2a82bc24ebdb87f52144ed). โครงร่าง 108 ไบต์ปัจจุบันถูกเพิ่มขึ้น drand_round และ QUB_ID_V2 ตัวคั่นโดเมน เวกเตอร์ 108 ไบต์ยุคแรกที่ใช้แบบเก่า ceil การทำแผนที่แบบรอบdrand_round = 4695445) และผลิต 3a9fcb31b750d985c262fada6d4f777fd6a28be831d941d85c131f5a4bbaf8a4—ยังใช้ได้อยู่ qub_id สำหรับข้อมูลป้อนรอบนั้น ในขณะที่ตัวอย่างข้างต้นทำตามการแมปของรอบปัจจุบัน §4.3
14.2 การแมปการปลดล็อก-รอบ
Input:
unlock_at = 1735689600
chain_genesis_time = 1595431050
chain_period_seconds = 30
Calculation:
(1735689600 - 1595431050) / 30 = 4675285.0
floor(4675285.0) + 1 = 4675286
drand_round = 4675286
รอบ 4675286 ตีพิมพ์ที่ 1595431050 + (4675286 - 1) * 30 = 1735689600—ตรงเวลา unlock_at, ไม่เคยมีมาก่อน (เวอร์ชันทดลองก่อนวางจำหน่ายของรุ่นดั้งเดิม ceil การทำแผนที่ให้ 4675285, เผยแพร่เมื่อ 1735689570—เร็วกว่า 30 วินาที; ผู้ตรวจสอบยอมรับรอบเดิมตาม §4.3.)
14.3 การไปกลับของ CBOR มาตรฐาน
การใช้งานต้องตรวจสอบว่า serialize(parse(serialize(qub))) == serialize(qub) สำหรับอินพุตที่ถูกต้องทั้งหมด นี่คือการทดสอบคุณสมบัติ ไม่ใช่เวกเตอร์เดียว
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)
ไบต์ CBOR มาตรฐานและ SHA3-256 body_hash ถูกคำนวณโดยการใช้งานอ้างอิง การใช้งานต้องสร้าง CBOR ที่เป็นไบต์เหมือนกันสำหรับอินพุตนี้
การใช้งานต้องตรวจสอบด้วยว่า serialize(parse(serialize(pact))) == serialize(pact) สำหรับอินพุต PactTerms ที่ถูกต้องทั้งหมด (การทดสอบคุณสมบัติ)
14.5 เวกเตอร์ข้ามภาษาของตัวห่อหุ้มภายนอก
ตัวห่อหุ้มภายนอก (§13) มี fixture มาตรฐานแยกที่ crates/qub-core/tests/vectors/wrapper_v1.json แต่ละกรณีกำหนด tuple (key, nonce, qub_id, sealed_cbor) เป็นอินพุตเลขฐานสิบหกทึบแสงและยืนยันเอาต์พุต expected_wrapper_hex ที่เฉพาะเจาะจง การใช้งานอ้างอิงทั้งสองบริโภคไฟล์ 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)
อุปกรณ์ติดตั้งปัจจุบันยึดสามกรณีตัวหุ้มระดับต่ำ พวกมันทดสอบความแน่นอน OuterWrapper การเข้ารหัสและความสามารถในการทำงานร่วมกันของ AEAD อย่างอิสระจากตัวแปรรูปแบบการส่ง §13.8; โดยเฉพาะชื่อในประวัติศาสตร์ basic-text-public และด้านในของมัน visibility = 0x01 ทำ ไม่ ทำให้ไบต์ที่ถูกห่อหุ้มที่ได้เป็นการส่งมอบสาธารณะที่สอดคล้อง ผู้ผลิตยังต้องเก็บไบต์ภายในสาธารณะแบบเปลือยและห่อเฉพาะไบต์ส่วนตัว (0x00) ไบต์ภายใน
| กรณี | ความคุ้มครอง |
|---|---|
basic-text-public |
ชื่ออุปกรณ์ระดับต่ำในประวัติศาสตร์ ขนาดเล็กที่สุดที่เป็นไปได้ SealedQub รูปแบบ โดยไม่มีฟิลด์ทางเลือก; ทดสอบเฉพาะไบต์ตัวห่อหุ้มและไม่ใช่การจัดส่งที่สอดคล้องกับ §13.8 |
with-recipient-pubkey |
SealedQub กับ recipient_pubkey ตั้งค่า (เส้นทางในอนาคตที่สงวนไว้) ฝึกใช้งานชุดกุญแจ CBOR ภายในที่แตกต่างกัน; เนื้อหาอุปกรณ์ติดตั้งที่แตกต่างกันของมันให้ผลลัพธ์ที่แตกต่างกันอย่างอิสระ qub_id (recipient_pubkey ตัวมันเองไม่ได้อยู่ในภาพก่อนของ §4.1 |
longer-body |
~4 KiB ของเนื้อหา — ฝึกซ้อมคำนำความยาว CBOR หลายไบต์ทั้งในซองด้านในและข้อความเข้ารหัสด้านนอก |
การใช้งานต้องสร้าง expected_wrapper_hex ที่เป็นไบต์เหมือนกันสำหรับอินพุตที่บันทึกไว้ การสร้าง fixture ใหม่ต้องใช้ QUB_REGEN_VECTORS=1 cargo test -p qub-core --test wrapper_vectors และสงวนไว้สำหรับการเปลี่ยนแปลงรูปแบบโดยเจตนา
15. การกำกับดูแลโปรไฟล์การเข้ารหัส (อนาคต)
ส่วนนี้เป็นข้อมูลสำหรับ v1 และกลายเป็นเชิงบรรทัดฐานในครั้งแรกที่อัลกอริทึมที่สองเข้าสู่หนึ่งในองค์ประกอบพื้นฐานทางการเข้ารหัสของ qub
15.1 ท่าทีปัจจุบัน
โปรโตคอล v1 ผูกอัลกอริทึมหนึ่งตัวต่อหนึ่งองค์ประกอบพื้นฐานพอดี:
- ลายเซ็น: ML-DSA-65 (
sig_alg = 0x01; กุญแจสาธารณะ 1952 ไบต์, ลายเซ็น 3309 ไบต์) และไม่ได้ลงนาม (sig_alg = 0x00). ฐานรหัสสงวน0x02สำหรับ Ed25519 แต่โปรโตคอล v1 ไม่เปิดใช้งานมัน; ตัวตรวจสอบ v1 ต้องปฏิเสธทุกกรณีsig_algข้างนอก{0x00, 0x01}. - การล็อกเวลา: drand quicknet เท่านั้น — แฮชของเชน, กุญแจสาธารณะ, เวลาเริ่มต้น, และช่วงเวลาคือพารามิเตอร์เครือข่ายที่ถูกกำหนดไว้ซึ่งถูกนำโดยอ้างอิง
DrandTimelockProvider::quicknet()(crates/qub-core/src/tlock.rs) และconfig/drand-endpoints.json. - ห่อชั้นนอก: AES-256-GCM รุ่น 1 (§13)
ตัวตรวจสอบปัจจุบันตั้งค่าความยาวของกุญแจและลายเซ็นต์ไว้แบบตายตัวตามพร้ามิทิฟที่ใช้งานอยู่ sig_alg และไบต์ของ wrapper-version เป็นตัวเลือกที่ชัดเจน แต่ v1 ไม่ทำการต่อรองในแถบสัญญาณและยอมรับเฉพาะค่าที่ใช้งานอยู่ด้านบนเท่านั้น
15.2 รูปร่างที่ตั้งใจ
เมื่ออัลกอริทึมที่สองเข้าสู่โปรโตคอล ตัวตรวจสอบจะถูกกำหนดค่าสำหรับ CryptoProfile ที่มีชื่อ (เช่น ExqubV1) ที่แสดงรายชุดค่าที่อนุญาตที่แม่นยำต่อองค์ประกอบพื้นฐาน — sig_algs, drand chain, เวอร์ชันตัวห่อหุ้ม, ประเภทเนื้อหา โปรไฟล์ถูกแก้ไขที่เวลาตรวจสอบ ไม่เคยถูกเจรจาในแบนด์ ค่าใดก็ตามที่อยู่นอกโปรไฟล์ที่ใช้งานจะถูกปฏิเสธ
สิ่งนี้รับประกันว่าการเพิ่ม ML-DSA-87 หรือการเปิดใช้งาน Ed25519 ไม่สามารถลดความเข้มแข็งของการตั้งค่าตัวตรวจสอบที่มีอยู่ย้อนหลังได้: ตัวตรวจสอบ v1 ยังคงเป็นตัวตรวจสอบ v1 แม้หลังจากที่โปรไฟล์ v2 ถูกเผยแพร่
15.3 เงื่อนไขการกระตุ้น
ยกระดับ §15 เป็นสถานะเชิงบรรทัดฐานเมื่อมีการเสนอข้อใดข้อหนึ่งต่อไปนี้:
- หนึ่งวินาที
sig_algไบต์ (การเปิดใช้งาน Ed25519, ML-DSA-87, หรือรายการใหม่ใด ๆ ในทะเบียน §9) - โซ่ drand ที่สองที่นำไปใช้ในการผลิต
- เวอร์ชันตัวห่อด้านนอกที่สอง
- การหมุนของรากความไว้วางใจของบันทึกความโปร่งใส —
LogProfile.anchor_ownerที่อยู่หรือคีย์สาธารณะของใบเสร็จที่ปักหมุด (§16.6).LogProfileเข้าร่วมพื้นผิวโปรไฟล์ §15.2 เป็นปริมาตรที่ควบคุม: การหมุนเป็นค่าที่มีเครื่องหมายLogProfilebump ถูกจัดส่งในการอัปเดตตัวตรวจสอบ (การสลับหมุนที่วางแผนไว้ cross-sign จากขาออก → ขาเข้า; การสลับหมุนที่เกิดจากการถูกบุกรุกไม่สามารถทำได้ และต้องพึ่งพา bump นี้พร้อมกับการตรวจสอบส้อม prev-anchor เพื่อลดความเสียหายชั่วคราว) พื้นที่เวอร์ชันของ transparency-log (LOG_VERSION,ANCHOR_FORMAT) พัฒนาขึ้นเป็นพี่น้องอิสระ เหมือนกับที่เวอร์ชันห่อ §12.5 เป็นอิสระจากเวอร์ชันโปรโตคอล
จนกว่าจะถึงเวลานั้น §15 เป็นตัวยึดที่กำหนดรูปร่างการย้ายเพื่อให้ PR ในอนาคตลงบนเป้าหมายที่รู้จักแทนที่จะอภิปรายซ้ำเกี่ยวกับพื้นผิวการเจรจาตั้งแต่ต้น
16. บันทึกความโปร่งใสและระดับความคงทน (การออกแบบ — ตรวจทานเสร็จสมบูรณ์)
สถานะ ส่วนนี้คือ ดำเนินการ (W5/UP-B1, ขั้นตอน 1–8) พร้อมกับผู้ออกและขอบเขตของ trust-root ที่ระบุไว้ที่นี่ รูปแบบสายข้อมูล การแฮช และเส้นทางการยืนยันอยู่ในสถานะใช้งาน: ประเภท Merkle หลัก + canonical-CBOR (
qub-core), ตัวสะท้อน TypeScript + ตัวรวม ANS-104 (workers/api/src/crypto/), ผู้เขียนเพียงคนเดียวLogDO+ ร้านเก็บโหนด R2 แบบใช้คีย์พิกัด/uploadพยายามต่อท้ายบันทึก, ตัวจับรายวัน + งานซักโครนของ bundler-drain,GET /api/v1/qub/:tx_id/proof(การรวม) และGET /api/v1/log/consistency(RFC 9162) จุดสิ้นสุดของหลักฐาน, หลักฐานการรวมประเภทที่ถูกพกพาใน.qubแพ็กเกจ (§17.5), ตัวตรวจสอบสมอ ANS-104 ดั้งเดิม (tools/qub-verify), และตะขอสองหัวแบบตีพิมพ์เอง (§16.6). ที่ประสบความสำเร็จ/uploadมักทนทานต่อ R2 เสมอ แต่จะมีผิวปกคลุมด้วยเศษไม้เฉพาะเมื่อLOG_DOถูกกำหนดค่าและการแนบแบบอินไลน์สำเร็จ; เฉพาะตอนนั้นการตอบกลับของมันจึงมีผลlog_seq,receipt, และanchor_status. ถ้าRECEIPT_SKขาดหรือไม่ถูกต้อง ใบนั้นsig_b64urlว่างและไม่ให้ความสามารถในการปฏิเสธไม่ได้ ปัจจุบัน/sealและกำหนดเส้นทางการเผยแพร่ข้อตกลง ตารางการทำธุรกรรม Arweave รายบุคคล แต่ไม่แนบใบไม้บันทึก ปัจจุบันไม่มีโค้ดใดที่ทำงาน/uploadข้อเสนอความคิดเห็นเกี่ยวกับการประนีประนอมในภายหลังหลังจากความล้มเหลวในการเพิ่มข้อมูล การทบทวนภายนอก W5 เสร็จสมบูรณ์แล้ว: §16.15 บันทึกการตัดสินใจด้านการออกแบบและข้อจำกัดในการเปิดตัว แต่ข้อจำกัดเหล่านั้นไม่ขยายความครอบคลุมของผู้ผลิตที่ระบุไว้ก่อนหน้านี้ มีรายการความเชื่อมั่น/การปรับใช้คงเหลืออีกสามรายการที่ยังถูกจำกัด: (a) กระเป๋าเงินสมอที่จัดสรรไว้ (ANCHOR_JWK;LogProfile.anchor_ownerยังคงเป็น[0xAB; 32]ตัวแทน); (b) กุญแจลงนามรับทราบ และการจับคู่พินกุญแจสาธารณะ (RECEIPT_SKเป็นทางเลือกและLogProfile.receipt_pubkeyขณะนี้ว่างเปล่า); และ (c) รีโพซิทอรี GitHub ของ self-published-heads + โทเค็น (§16.6) จนกว่าจะมีการจัดเตรียม anchor/profile pins ตัวตรวจสอบแบบสแตนด์อโลนจะแจ้งสถานะการพิสูจน์อย่างตรงไปตรงมามากกว่าการอ้างสิทธิ์การตรวจสอบที่ยึดและปักหมุดครบถ้วน การออกแบบเป็นแบบเพิ่มตามลำดับและมี ไม่มีการเปลี่ยนแปลงต่อSealedQub/QubEnvelopeรูปแบบสาย.
16.1 เหตุผลและระดับความคงทน
เส้นทางการเผยแพร่ปัจจุบันแยกการยืนยันออกจากการยืนยันของ Arweave: พวกมันสร้างและลงนามในธุรกรรมแต่ละรายการ เก็บสิ่งประดิษฐ์และสถานะการส่งที่แน่นอนใน R2 จากนั้นจึงโพสต์แบบอะซิงโครนัส บันทึกความโปร่งใสเพิ่มชั้นการจัดลำดับที่ยึดอย่างอิสระสำหรับชุดย่อยของทั่วไป /upload คำขอที่ LogDO การต่อสำเร็จ:
| ระดับ | ชื่อ | การรับประกัน | เมื่อ |
|---|---|---|---|
| ที1 | R2-การยืนยันแบบซิงโครนัสครั้งแรก | ความทนทานของข้อมูลพื้นฐาน — ไบต์ที่ถูกปิดผนึกและสถานะการเผยแพร่ที่ถูกต้องจะถูกบันทึกลงในสตอเรจที่ทนทานก่อนที่จะส่งผลลัพธ์ความสำเร็จกลับ | นำไปใช้ในเส้นทางการเผยแพร่ปัจจุบัน |
| ที2 | การรวมบันทึกความโปร่งใสแบบเป็นชุด | การยึดมั่นแบบเพิ่มอย่างเดียว ตรวจสอบการปลอมแปลงได้ + การจัดลำดับทั้งหมดเมื่อรวมเข้าและยึดแน่นแล้ว | ผู้ผลิตปัจจุบัน: ประสบความสำเร็จ LogDO ต่อจาก /upload; การตอบสนองมีทูเพิลใบเสร็จ ไม่ใช่อเนกประสงค์ |
| ที3 | ความถาวรของ Per-qub Arweave | ธุรกรรม Arweave เดี่ยวสำหรับ qub | ปัจจุบันเตรียมไว้สำหรับการตีพิมพ์ที่ได้รับการยอมรับทุกฉบับและโพสต์แบบไม่พร้อมกัน; ธุรกรรมที่ลงนามอย่างถูกต้องจะยังคงอยู่ในกล่องส่งออกที่สามารถระบายน้ำได้จนกว่าจะส่งมอบ |
ชั้นต่าง ๆ อธิบายคุณสมบัติของหลักฐานและความทนทานที่แตกต่างกัน ไม่ใช่แผนการค้าปัจจุบัน โค้ดปัจจุบันยังคงกำหนดเวลาธุรกรรม Arweave แต่ละรายการสำหรับการตีพิมพ์ที่ยอมรับทุกครั้ง; มันไม่ได้เปิดเผย T3 เพียงแค่เป็นการอัปเซลแบบชำระเงิน ขีดจำกัดโควต้าของคีย์ API/บัญชียังคงเป็นการควบคุมของแอปพลิเคชันแยกต่างหาก
ความทนทาน ความซื่อสัตย์ การเขียน T1 เป็นแบบซิงโครนัส ดังนั้นการตอบสนองที่ประสบความสำเร็จจะสร้างความทนทานในระดับแอปพลิเคชันโดยไม่ต้องรอเกตเวย์ Arweave มันเองไม่ได้สร้างเครื่องหมายเวลาอิสระ การทำธุรกรรมแต่ละรายการที่ได้รับการยืนยันจะให้ค่าขอบบนของเวลาในบล็อก สำหรับการตอบสนองที่มีชุดใบเสร็จ T2 ครบถ้วน anchor ที่ได้รับการยืนยันในครั้งถัดไปสามารถให้หลักฐานบันทึกตามที่อธิบายไว้ด้านล่าง หากชุดใบเสร็จขาดหายไป จะไม่มีสัญญาณใดที่บ่งบอกว่า qub นี้อยู่ในบันทึกความโปร่งใสแล้ว ความล่าช้าของ anchor และการเผยแพร่ไม่มี SLA ทางตัวเลขในระดับโปรโตคอล
16.2 โครงสร้าง LogLeaf (สองรูปทรงที่ผูกพัน)
รายการบันทึกคือ LogLeaf เข้ารหัสเป็น CBOR มาตรฐานที่เขียนด้วยมือภายใต้โปรไฟล์ §3.1 (ความยาวกำหนดได้ ไม่มี tag ไม่มี float จำนวนเต็มรูปแบบสั้นที่สุด ข้อความ NFC ฟิลด์เลือกได้ถูกละเว้นเมื่อไม่มี กุญแจเรียงตามความยาวไบต์ที่เข้ารหัสจากน้อยไปมากแล้วเรียงตามไบต์) ตัวป้องกันมาตรฐาน parse → re-encode → compare ของ §3.1 ถูกใช้ บนเส้นทางเข้ารหัสก่อนการแฮช (ไม่ใช่เฉพาะบนการถอดรหัส) ดังนั้นการใช้งานสองตัวจึงไม่สามารถขัดแย้งกันเรื่องไบต์ของลีฟผ่านความแตกต่างของความกว้างจำนวนเต็มหรือลำดับกุญแจได้ จำนวนเต็มทั้งหมดเป็น u8 / u64 / i64; ไดเจสต์ทั้งหมดเป็นสตริงไบต์ 32 ไบต์ (bstr[32]) id ธุรกรรม Arweave ที่จัดเก็บเป็นไดเจสต์ SHA-256 ดิบ 32 ไบต์ที่พกพาเป็น bstr[32] ไม่เคย เป็นสตริงข้อความ base64url (ตรงกับ §3.3)
ใบไม้มี สองรูปทรงที่ถูกเลือกโดย kind ไบต์, เพราะเส้นทางการอัปโหลดทั่วไปเป็นแบบบอดไบต์: POST /api/v1/upload จงใจปฏิบัติต่อรูปแบบข้อมูลส่งที่ได้รับการยอมรับทั้งสองแบบเหมือนเป็นสิ่งที่มองไม่เห็นและรับมัน qub_id และ unlock_at เท่านั้นในฐานะ คำยืนยันของลูกค้าที่ไม่น่าเชื่อถือ บนเส้นทางส่วนตัวเริ่มต้น, body_hash, drand_round, created_at, และ drand_chain_version ยังซ่อนอยู่ภายในตัวห่อด้านนอก §13 ซึ่งกุญแจนั้นคนงานไม่เคยถือ ระบบประเภทยังได้กำหนดรูปร่างที่ได้รับการรับรองสำหรับผู้ผลิตที่สืบทอด body_hash / drand_round ตัวมันเอง ปัจจุบัน /seal เส้นทางมีค่านั้นแต่ไม่ได้เรียก LogDO, ดังนั้นการผลิตในปัจจุบันจึงปล่อยออกมาเฉพาะที่ได้รับการยืนยัน (0x02) ทิ้งจากการแนบการอัปโหลดทั่วไปที่ประสบความสำเร็จ การแยกช่วยให้ค่าที่มุ่งมั่นทุกค่าซื่อสัตย์โดยไม่แสร้งว่าผู้ผลิตที่ได้รับการยืนยันเชื่อมต่อ:
| กุญแจ | ความยาว Enc. | ประเภท | การปรากฏตัว | ความหมาย |
|---|---|---|---|---|
seq |
สี่ | u64 |
จำเป็น | ดัชนีใบทั่วโลกแบบเริ่มต้นจากศูนย์; ตำแหน่งที่การพิสูจน์การรวมผูกมัด |
kind |
ห้า | u8 |
จำเป็น | 0x01 สามารถยืนยันได้ (กำหนดไว้ แต่ไม่ได้ปล่อยออกมาในขณะนี้) หรือ 0x02 ยืนยัน (การอัปโหลดแบบปิดผนึกลูกค้าหรือบลายต์) |
ref |
สี่ | bstr[32] |
จำเป็น | รหัสอ้างอิงใบไม้ ได้รับรอง → ดิบ qub_id. ยืนยัน → the ตาบอด รหัสประจำตัว SHA3-256(qub_id ‖ log_blind_secret) (§16.2.1). |
chash |
หก | bstr[32] |
จำเป็น | ที่อยู่เนื้อหา SHA3-256(stored_bytes) — สิ่งผูกมัดเดียวที่ผู้ปฏิบัติงานสามารถคำนวณอย่างตรงไปตรงมาได้เสมอ ในทั้งสองเส้นทาง |
unlock_at |
สิบ | i64 |
จำเป็น | คัดลอก (พิสูจน์แล้ว) หรือยืนยัน (ยืนยันแล้ว); ตรวจสอบความถูกต้อง > 0 ก่อนที่มันจะเข้าสู่ใบไม้ |
received_at |
สิบสอง | i64 |
จำเป็น | นาฬิกาผนังของคนงานที่ R2-ack. ไม่เป็นหลักฐาน (ผู้ดำเนินงานยืนยัน; §16.6) ใช้สำหรับการบรรยายตนเอง ไม่ใช่หลักฐาน ใช้ตรวจสอบแล้ว > 0. |
body_hash |
สิบ | bstr[32] |
kind=0x01 เท่านั้น |
ถูกละเว้นเมื่อ 0x02 — พนักงานขาดสิทธิ์นั้นตามมาตรา 13 |
drand_round |
สิบสอง | u64 |
kind=0x01 เท่านั้น |
ถูกละเว้นเมื่อ 0x02. |
ลีฟ kind=0x02 จงใจไม่ผูกพันทั้ง body_hash และ drand_round: มันรับรอง ข้อผูกพันและการจัดลำดับของไซเฟอร์เท็กซ์ทึบแสงที่ที่อยู่เนื้อหา chash โดยอ้าง qub_id และ unlock_at — ไม่ใช่ข้อความธรรมดาหรือรอบของมัน ขาข้อความธรรมดา/รอบสำหรับ qub ที่กล่าวอ้างมาจากการตรวจสอบ .qub-bundle §11 ที่มีอยู่ ไม่ใช่จากบันทึก (§16.11) drand_chain_version ไม่อยู่ในลีฟ (มันอยู่ภายในตัวห่อหุ้มบนเส้นทางเริ่มต้น); ความละเอียดของเชนอยู่บนจุดยึด (§16.7) ระเบียบวินัยของตัวเข้ารหัส: ปฏิเสธ ref หรือ chash ที่เป็นศูนย์ทั้งหมด และปฏิเสธ unlock_at / received_at ที่ไม่เป็นบวก สะท้อนตัวป้องกันค่าเซนติเนล outcome_at > 0 ใน cbor.rs
16.2.1 การปิดบัง qub ส่วนตัว
บันทึกต้องไม่กลายเป็นออราเคิลแจกแจงที่ตัวห่อหุ้มภายนอก §13 มีไว้เพื่อป้องกัน (§13.1) สำหรับ qub ส่วนตัว (ที่ห่อหุ้ม) ลีฟ asserted ผูกพัน ตัวระบุที่ปิดบัง SHA3-256(qub_id ‖ log_blind_secret) โดยที่ log_blind_secret เป็นความลับที่เซิร์ฟเวอร์ถือไว้ และละเว้น body_hash บุคคลที่สามไม่สามารถผูกลีฟดังกล่าวเข้ากับ qub_id ที่เฉพาะเจาะจงได้; ผู้ถือ qub ซึ่งมี URL ส่งและจึงมี qub_id สามารถคำนวณการปิดบังใหม่เพื่อยืนยันการรวมของตัวเอง qub สาธารณะ (แจกแจงได้อยู่แล้ว มี tag Arweave Visibility: public ตาม §13.8 อยู่แล้ว) ผูกพัน qub_id ดิบ นี่คือจุดเดียวที่ความสามารถในการตรวจสอบแบบยืนเดี่ยวจงใจยอมให้กับค่าคงที่ความเป็นส่วนตัวที่รับน้ำหนัก; การผูกแบบยืนเดี่ยวสำหรับ qub ส่วนตัวคือ chash (§16.9)
การเก็บรักษา log_blind_secret (ได้ข้อสรุป — §16.15 Q4) การปิดบังปกป้อง การไม่สามารถเชื่อมโยงลีฟ ไม่ใช่ความลับของข้อความธรรมดา (ตัวห่อหุ้ม §13 ถือสิ่งนั้นไว้อย่างอิสระ) เมื่อ log_blind_secret ถูกบุกรุก สำหรับ qub_id ใด ๆ ที่ฝ่ายตรงข้ามถือหรือสร้างใหม่ได้อยู่แล้ว (ทุก qub ที่มี bundle/URL ของมัน รวมถึง qub_id สาธารณะหรือเอนโทรปีต่ำใด ๆ) มันคำนวณ ref ของลีฟใหม่ใน การแฮชเดียว และเชื่อมโยงมัน — นี่คือการเชื่อมโยงโดยตรงของประชากรที่รู้จัก ไม่ใช่การลองทุกค่าบนพื้นที่ที่ไม่รู้จัก จัดประเภท log_blind_secret เป็นความลับระดับความสัมพันธ์/Sybil ในชั้นการเก็บรักษาเดียวกับความลับเซิร์ฟเวอร์อื่น ๆ และหมุนไปข้างหน้าเท่านั้น (การหมุนปิดบังลีฟในอนาคตใหม่; มันไม่สามารถยกเลิกการเชื่อมโยงลีฟที่ยึดแล้วย้อนหลังได้)
16.3 การแฮชลีฟและโหนด
การแฮชแบบแยกโดเมนตาม RFC 6962 §2.1 โดยแทนที่ SHA-256 ด้วย 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
ไบต์คำนำหน้าโดเมน 0x02 (เชนรายการ §16.4) และ 0x03 (แฮช STH §16.6) ถูกสงวนและแยกออกจากเหล่านี้ พวกมันเป็นไบต์เดี่ยวจึงไม่สามารถชนกับตัวแยกโดเมน ASCII 10 ไบต์ที่มีอยู่ได้ (QUB_ID_V2 เป็นต้น) ต้นไม้คือต้นไม้ ไม่สมดุลแบบเต็มซ้าย ตาม RFC 6962 (แต่ละการแยกภายในที่กำลังสองสูงสุดที่น้อยกว่าจำนวนลีฟของต้นไม้ย่อยอย่างเคร่งครัด) ซึ่งทำให้หลักฐานการรวมและความสอดคล้องใช้อัลกอริทึมเส้นทางตรวจสอบเดียวกันได้ ข้อกำหนดอ้างอิงพกซูโดโค้ดการหาค่าซ้าย/ขวาอย่างชัดเจน และตรึง เวกเตอร์ทดสอบที่ไม่ใช่กำลังสอง (5 ลีฟ) ดังนั้นกรณีการเลื่อนขอบขวา — ซึ่งเวกเตอร์ 4 ลีฟซ่อนไว้ — จึงถูกใช้งาน
16.4 การเชื่อมโยงแฮช (ภายใน)
LogDO ดูแลเชนรายการภายในเพื่อความสอดคล้องเมื่อแครชเท่านั้น มัน ไม่เคยถูกเผยแพร่และไม่เคยหันสู่ตัวตรวจสอบ:
entry_chain[seq] = SHA3-256(0x02 || entry_chain[seq-1] || leaf_hash[seq])
entry_chain[-1] = SHA3-256("QUB_TLOG_GENESIS_V1")
อำนาจเพิ่มต่อท้ายเท่านั้นที่เผยแพร่คือ รากเมอร์เคิลสะสม + จุดยึดของมัน (§16.5–16.6) ไม่เคยเป็นลำดับดิบที่ผู้ดำเนินการบังเอิญให้บริการลีฟ: เชนคำนวณใหม่สำหรับลำดับใด ๆ ที่ให้บริการ ดังนั้นมีเพียงรากที่ยึดแล้วเท่านั้นที่ตรึงตำแหน่งมาตรฐาน
16.5 ต้นไม้เมอร์เคิลสะสมและการรวมเป็นชุด
มี ต้นไม้ RFC 6962 ที่เติบโตตลอดเวลาหนึ่งต้น เหนือลีฟทั้งหมดตามลำดับ seq — ไม่ใช่ต้นไม้แยกต่อชุด (การสร้างแบบเชนลีฟพ่วงต่อชุดถูกปฏิเสธ: มันไม่ใช่ความสัมพันธ์คำนำหน้าที่แท้จริง ดังนั้น "หลักฐานความสอดคล้อง" ของมันจึงไม่สมเหตุสมผล) ต้นไม้สะสมให้หลักฐานความสอดคล้อง RFC 9162 ของแท้และทำให้จุดยึดล่าสุดเพียงจุดเดียวสามารถพิสูจน์การรวมสำหรับ qub เก่ากว่าใด ๆ ได้
นั้น LogDO วัตถุที่ทนทานคือ นักเขียนคนเดียว (blockConcurrencyWhile, สะท้อน QuotaDO / EntitlementDO) — การแนบไปยังบันทึกร่วมถือเป็นการอ่าน-ปรับ-เขียนบนสภาวะร่วม ดังนั้นต้องผ่าน DO เสมอ ห้ามผ่าน KV มันจะเก็บแคชขอบด้านขวาของต้นไม้ (O(log n) แฮช) ดังนั้นการปิดชุดคือ O(batch). ก ชุด คือชุดของใบไม้ที่ยึดติดกัน; ทริกเกอร์ที่นำไปใช้งานคือ tree_size ล่วงหน้าอย่างน้อย LOG_BATCH_MAX_LEAVES (ค่าเริ่มต้น 4096), อายุที่ถึงจังหวะสมอ หรือการบังคับปิดโดยผู้ดูแลระบบ/cron อย่างชัดเจน root_i คือแฮชเมอร์เคิลทรีสะสมเหนือใบไม้ 0 .. tree_size_i.
16.6 Signed Tree Head ผ่านจุดยึด Arweave
ธุรกรรมจุดยึด Arweave คือ Signed Tree Head และ แทนที่ ลายเซ็นผู้ดำเนินการ สำหรับหัวต้นไม้นั้นเอง: จุดยึดรายวันไม่ต้องการกุญแจ qub เพราะ owner ของธุรกรรม Arweave คือลายเซ็น แนวคิดคูเมืองยังคงอยู่ — สารตั้งต้นที่เปลี่ยนแปลงไม่ได้ ไม่ใช่ความลับที่ qub ถือ คือสิ่งที่รับน้ำหนักสำหรับรากที่ยึดแล้ว
การออกแบบบันทึกเรียกร้องให้มีการแนบสำเร็จแบบอบอุ่นหนึ่งครั้ง กุญแจใบเสร็จ (§16.10), ติดอยู่ใน LogProfile และลงนามข้ามโดย anchor_owner. การใช้งานในปัจจุบันยังไม่ได้ดำเนินการจัดหารากความเชื่อที่นั่นเสร็จสิ้น: RECEIPT_SK เป็นทางเลือก กุญแจที่หายไป/ไม่ถูกต้องจะให้ผลลัพธ์ sig_b64url: "", และที่รวบรวมแล้ว LogProfile.receipt_pubkey ว่าง ใบเสร็จดังกล่าวสามารถอธิบายแผ่นแนบได้แต่ ไม่ ใบเสร็จที่ลงนามซึ่งไม่สามารถปฏิเสธได้ ข้อเรียกร้องการออกแบบที่แข็งแกร่งขึ้นจะใช้ได้ต่อเมื่อผู้ตรวจสอบปล่อยพินที่จับคู่กับกุญแจสาธารณะและเจ้าของสมอเซ็นข้ามแล้วเท่านั้น การตอบสนองการเผยแพร่โดยไม่มีชุดใบเสร็จสมบูรณ์จะไม่ทำข้อเรียกร้องการยอมรับบันทึก; การตอบสนองที่มีลายเซ็นว่างจะทำข้อเรียกร้องตำแหน่งการแนบ แต่ไม่ทำข้อเรียกร้องการตรวจสอบลายเซ็น
SignedTreeHead เป็น CBOR มาตรฐาน (กุญแจตามความยาวเข้ารหัส): size:u64, root:bstr[32], batch:u64, prev:bstr[32] (sth_hash ก่อนหน้า; genesis = 32 ไบต์ศูนย์), log_id:bstr[32], first_seq:u64, anchored_at:i64 แฮชของมันคือ sth_hash = SHA3-256(0x03 || canonical_cbor(SignedTreeHead))
รากความเชื่อถือที่ตรึง log_id = SHA3-256("QUB_TLOG_V1" || anchor_owner_address) ตัวตรวจสอบที่สอดคล้องต้องกำหนด anchor_tx.owner == LogProfile.anchor_owner โดยที่ anchor_owner (และกุญแจสาธารณะกุญแจใบเสร็จ) ถูกอบเข้าใน qub_core เป็น LogProfile — ควบคู่กับค่าคงที่ quicknet ที่อยู่ใน DrandTimelockProvider::quicknet() อยู่แล้ว — และแจกจ่ายพร้อมไบนารีตัวตรวจสอบ ตัวตรวจสอบต้องตรวจสอบ การผูก data → tx_id ของธุรกรรม Arweave ในเครื่อง ด้วย แทนที่จะเชื่อถือการตอบสนอง /raw/ ของเกตเวย์ สิ่งนี้ปิดช่องการให้ข้อมูลขัดแย้งของกระเป๋าเงินทุจริต: "ยึดบน Arweave แล้ว" ไม่มีความหมายจนกว่าตัวตรวจสอบจะตรึง ว่ากระเป๋าเงินใด
การหมุนเป็น ส่วนขยาย การกำกับดูแล §15 ไม่ใช่การนำกลับมาใช้ซ้ำ (ได้ข้อสรุป — §16.15 Q3) พื้นผิวโปรไฟล์ของ §15.2 ปัจจุบันแจกแจงเฉพาะ sig_algs / drand chains / เวอร์ชันตัวห่อหุ้ม / ประเภทเนื้อหา และตัวกระตุ้นของ §15.3 ไม่ระบุค่าเหล่านี้เลย — LogProfile / anchor_owner ยังไม่ อยู่ในพื้นผิวของ §15 การกำกับดูแลการหมุนจึงต้อง สร้าง: §15.3 ถูกขยาย (ด้านล่าง) เพื่อเพิ่มตัวกระตุ้น LogProfile และการหมุนคือการเพิ่ม LogProfile ที่ลงนามแล้วจัดส่งในการอัปเดตตัวตรวจสอบ การหมุนที่ วางแผนไว้ พกลายเซ็นไขว้ออก → เข้า; การหมุนที่ เกิดจากการบุกรุก ทำไม่ได้ (กุญแจที่ออกไม่น่าเชื่อถือ/ไม่พร้อมใช้งานในตอนนั้นพอดี) และตกกลับสู่การเพิ่มที่กำกับโดย §15 โดยการตรวจสอบฟอร์กของจุดยึดก่อนหน้า (ด้านล่าง) จำกัดความเสียหายในช่วงระหว่าง
หน้าต่างความกำกวม (พารามิเตอร์ความไว้วางใจระดับหนึ่ง) ใบไม้จะต้านทานการกำกวมได้ก็ต่อเมื่อสมอคลุมของมันเป็นของ Arweaveยืนยันแล้ว. หน้าต่างคือ received_at → anchor confirmation (cadence + ความแน่นอนขั้นสุดท้ายของ Arweave โดยไม่มีการรับประกันความหน่วงของโปรโตคอล) ก่อนการจัดเตรียมรากฐานแห่งความเชื่อถือ การใช้งานปัจจุบันให้ความสมบูรณ์การทำงานของ qub บวกกับข้อมูลเมตาแนบที่ไม่ได้ลงนามใด ๆ ที่มีอยู่; มันไม่ได้ให้การรับประกันการปฏิเสธไม่ได้ตามแผน วัตถุสิทธิรับผิดชอบสามอย่างกำหนดการออกแบบที่สมบูรณ์ (โมเดลพยานคือการแก้ไขของ §16.15 Q2)
- ใบเสร็จประทับตรา (ขึ้นอยู่กับการจัดหา) — อนาล็อก SCT ถูกส่งคืนเมื่อการแนบบันทึกของการอัปโหลดสำเร็จ (§16.10) มันจะไม่สามารถปฏิเสธได้ก็ต่อเมื่อ
sig_b64urlไม่ว่าง และ ความสัมพันธ์ระหว่างกุญแจสาธารณะที่ตรงกัน/เจ้าของสมอถูกตรึงในผู้ตรวจสอบ พินโปรไฟล์การผลิตที่ว่างอยู่ในปัจจุบันไม่สามารถสนับสนุนคำตัดสินนั้นได้ การควบคุมนี้จะไม่ใช้กับชุดใบเสร็จที่ละเว้นหรือใบเสร็จที่ไม่ได้ลงนาม - วิธีการตรวจสอบที่เผยแพร่ + การเดินตามโซ่ก่อนหน้า — สมอ
prevโซ่ถูกเดินจากหัว→กำเนิด; ส้อม (สมอสองอันที่หนึ่ง)sizeด้วยความแตกต่างroot, หรือชำรุดprev) เป็นหลักฐานที่สามารถตีพิมพ์ได้ของพฤติกรรมไม่เหมาะสม การตรวจจับการเลี่ยงคำพูดเป็นความมุ่งมั่นในการปฏิบัติที่ได้ระบุไว้ ไม่ใช่สมมติฐานเงียบ - หัวที่ตีพิมพ์ด้วยตัวเองสองหัว — แต่ละหัวใหม่
{sth_hash, tree_size}ถูกโพสต์ไปยังที่ที่เฉพาะเจาะจงซึ่งเป็นของ qub ที่เก็บ GitHub สาธารณะ แบบเพิ่มได้อย่างเดียว (ขาตีพิมพ์ด้วยตนเองที่รับน้ำหนักได้และสามารถตรวจสอบการปลอมแปลงได้), โดยมีโพสต์โซเชียลเป็นเพียงการยืนยันตามความพยายามที่ดีที่สุดเท่านั้น การโพสต์ที่ล้มเหลวจะต้องมีการแจ้งเตือน (ไม่สามารถล้มเหลวโดยเงียบ) ดำเนินการแล้ว (ขั้นที่ 8) เป็นpublishHeadเกี่ยวกับสมอ cron (workers/api/src/utils/heads-publish.ts): aPUTไปยัง API ของเนื้อหาโดยไม่มีshaเป็นแบบเพิ่มอย่างเดียว (a422หมายความว่าหัวข้อได้ถูกเผยแพร่แล้ว ไม่ใช่การเขียนทับ); การเลือกเข้าใช้งาน / ถูกจำกัดการปรับใช้PUBLISH_HEAD_GITHUB_{TOKEN,OWNER,REPO}และอยู่นิ่งจนกว่าที่เก็บจะถูกจัดเตรียม ความล้มเหลวของ GitHub อย่างรุนแรงผ่านhealth_alertช่องและกระแทกตัวชี้วัดความล้มเหลวที่ทนทาน (m:tlog_publish_head_fail); ตัวสมอของ Arweave เองจะไม่ย้อนกลับเมื่อการเผยแพร่ล้มเหลว “ไม่ล้มเหลวนิ่ง” เป็นสิ่งที่รับประกันโดยมาตรการที่ทนทานนี้ — ซึ่งฝ่ายปฏิบัติการต้องแสดงผลในแดชบอร์ดแจ้งเตือน — แม้ว่าหน้าอีเมลแบบพยายามดีที่สุดจะไม่สามารถส่งถึงได้ ข้อจำกัดที่ซื่อสัตย์สองประการตามมาจาก “สมอล่วงหน้า” (cron จะเผยแพร่เฉพาะเมื่อขนาดมีการเพิ่มขึ้น): ความล้มเหลวชั่วคราวของ GitHub จะทิ้ง ช่องว่าง ในลำดับหัวที่เผยแพร่สำหรับขนาดนั้น — จำกัด ไม่เงียบ (มันทำการแบ่งหน้า) และเพราะว่าหัวแต่ละหัวสร้างต้นไม้ชุดย่อยเพิ่มเติม หลักฐานความสอดคล้อง §16.9 จึงเชื่อมช่องว่าง; สำคัญคือ หลักฐานความสอดคล้องนั้นถูกคำนวณจาก ต้นไม้ที่ยึดกับ Arweave อย่างมีอำนาจ ไม่ใช่มาจากพื้นผิวของ GitHub, ดังนั้นช่องว่างใน GitHub จึงไม่เคยทำให้ความสามารถในการตรวจสอบอ่อนลง การเติมข้อมูลย้อนหลังเพื่อเติมช่องว่างของหัวข้อที่เผยแพร่เป็นการปรับปรุงที่เลื่อนออกไป
ความซื่อสัตย์ผูกมัด (ข้อจำกัดที่ผูกพัน) เพราะ qub ควบคุมทั้งพื้นผิวการโพสต์ที่วางแผนไว้ นี่คือ ตีพิมพ์เอง, ไม่ได้รับการยืนยันโดยอิสระ ไม่มีผลิตภัณฑ์ การตลาด หรือพื้นทางกฎหมายใด ๆ ที่อาจอ้างว่าบันทึกนั้น “ได้รับการยืนยันโดยอิสระ” หลังจากที่ใบเสร็จ/โปรไฟล์/หัวเกตถูกจัดเตรียมแล้ว ข้ออ้างที่ได้รับอนุญาตคือว่า การพูดคลุมเครือสามารถตรวจจับได้ และการแนบลายเซ็นที่สำเร็จจะทิ้งหลักฐานการรับที่ปฏิเสธไม่ได้. ก่อนหน้านั้น ข้อเรียกร้องนั้นไม่สามารถใช้ได้ พยานอิสระแท้จริงจากบุคคลที่สามจะถูกเลื่อนให้ไปยังการปรับปรุงการกำกับดูแล §15 ในอนาคต
received_at เป็นค่าที่ผู้ดำเนินการกล่าวอ้าง และ ไม่มีการอ้างใดพึ่งพามันได้ — มันไม่เคยถูกแสดงเป็นหลักฐานหรือการยืนยันข้อพิพาทบนพื้นผิวผลิตภัณฑ์ / กฎหมาย / API / การเรนเดอร์หลักฐานใด ๆ เวลาบล็อก T ของจุดยึด Arweave เป็นตราเวลาที่ไม่ต้องเชื่อถือเพียงหนึ่งเดียว (ขอบเขตบนของ "บันทึกเมื่อ") การตรวจสอบความสมเหตุสมผลของมอนิเตอร์บน received_at ใด ๆ ต้องเทียบกับ T ไม่ใช่กับฟิลด์ STH anchored_at ที่ผู้ดำเนินการควบคุม; การตรวจสอบเช่นนั้นเป็นตัวป้องกันต่อบั๊กนาฬิกาของผู้ดำเนินการที่ซื่อสัตย์เท่านั้น ไม่ใช่ การควบคุมความรับผิดชอบต่อผู้ดำเนินการที่มุ่งร้าย (§16.15 Q5)
16.7 รูปแบบและจังหวะธุรกรรมจุดยึด
AnchorBundle คือ body ธุรกรรม Arweave แบบ CBOR มาตรฐาน เขียนผ่าน bundler §16.8: ver:u8, sth:bstr (ไบต์ SignedTreeHead มาตรฐาน), prev_anchor:bstr (id ธุรกรรมจุดยึดก่อนหน้าเป็นไบต์ดิบ; ละเว้นที่ genesis), chain_hash:tstr (drand chain ที่มีผลบังคับ — quicknet) และ สตรีมลีฟ-CBOR ของชุดตามลำดับ seq ดังนั้นจุดยึดจึงครบในตัว: มอนิเตอร์หาค่า root ใหม่จาก body โดยไม่พึ่งพา qub เลย (หากสตรีมลีฟใหญ่ขึ้นที่ปริมาณสูง การแก้ไขในอนาคตอาจผูกพันเฉพาะช่วงลีฟโดยอ้างอิง; บันทึกไว้ ไม่นำมาใช้ใน v1)
tag Arweave จงใจให้แจกแจงได้ — บันทึก มีไว้ เพื่อให้ค้นพบ ต่างจาก qub ส่วนตัว: App-Name: qub-tlog, Anchor-Format: 1, Log-Id: <hex>, Batch: <n>, Tree-Size: <n>, Root: <hex>, Prev-Anchor: <tx>, Content-Type: application/cbor tag เป็น คำใบ้ที่ไม่น่าเชื่อถือ; body CBOR เป็นอำนาจเดียว
จังหวะ: รายวันตามค่าเริ่มต้น ถูกทบทวนใหม่พร้อมปริมาณ (ตัวกระตุ้นขนาดจะย่อความถี่ที่มีประสิทธิผลเมื่อมีภาระ) ผู้ผลิตปัจจุบันไม่ได้ติดตั้งตะขอบังคับตราจ่ายเงิน กระเป๋าสตางค์แองเคอร์ ทุ่มเทและมีความเร็วต่ำ แยกจากกระเป๋าอัปโหลด — ต้องเป็นของมันเอง JWK ของตัวเอง (กุญแจเฉพาะ ไม่ใช่บทบาทเชิงตรรกะบนกระเป๋าอัปโหลด) ดังนั้นการถูกเจาะกระเป๋าอัปโหลดจึงไม่สามารถสร้างสมอปลอมได้ — โดยมีงบประมาณการทำธุรกรรมสมอต่อวันที่ตายตัว ท่าทีการดูแลรักษาได้รับการระบุอย่างชัดเจน: a ปุ่มลัดที่มีขอบเขตจำกัดพร้อมเบรกเกอร์วงจรที่แน่นและสมดุลต่ำ, ไม่ใช่ “เย็น” — กระเป๋าสตางค์ที่เซ็นชื่ออัตโนมัติทุกวันไม่สามารถเรียกว่าเย็นได้ และสเปกก็ไม่ได้แอบอ้างว่าเป็นอย่างอื่น
16.8 ANS-104 Bundler
ตัวเข้ารหัส ANS-104 DataItem ภายในและตัวลงนาม deep-hash ราว 300 บรรทัด Web Crypto เท่านั้น ไม่มี dependency npm (Turbo SDK ทั้งสองตัวสอบตกประตูซัพพลายเชน npm ci --ignore-scripts) เลย์เอาต์ไบต์ 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
การลงนามคือ deepHash ของ Arweave — ไดเจสต์ SHA-384 แบบเรียกตัวเอง (ข้อกำหนดการสื่อสารของ Arweave, crypto.subtle.digest("SHA-384")) เหนือ ["dataitem", "1", sig_type, owner, target, anchor, encoded_tags, data] — จากนั้น RSA-PSS เหนือ deep hash ด้วยกระเป๋าเงิน JWK ผ่าน crypto.subtle; id = base64url(SHA-256(signature)) SHA-384 ที่นี่ถูก กักกันเป็นองค์ประกอบพื้นฐานสำหรับการสื่อสาร Arweave เท่านั้น ไม่เคยเป็นองค์ประกอบพื้นฐานความเชื่อถือของ qub (§15 บันทึกรั้วนี้ไว้; การแฮชความเชื่อถือ qub เป็น SHA3-256 ตลอด)
เส้นทางรหัส ANS-104 ทำหน้าที่เครื่องจักรสำรอง/ระบายที่เลื่อนออกไปและเขียน AnchorBundle DataItems เส้นทางการเผยแพร่ปกติจะสร้างธุรกรรม Arweave ที่เซ็นชื่ออย่างแม่นยำก่อนและเก็บ JSON ของมันไว้ในกล่องส่งออกที่ทนทาน; การโพสต์โดยตรงเป็นการปรับแต่งความหน่วงเวลา และเส้นทางการระบายจะลองทำธุรกรรมเดียวกันซ้ำก่อนที่จะใช้ตัวสำรอง bundler ของมัน โครงการลายเซ็น (แก้ไขแล้ว — §16.15 Q8): v1 เซ็นด้วย RSA-PSS (ประเภทลายเซ็น 1) การนำกลไก JWK ของกระเป๋าเงิน Arweave ที่มีอยู่แล้วมาใช้ซ้ำ (ไม่มีการเก็บกุญแจยาวใหม่ใด ๆ สนับสนุนแนวคิด "ความลับน้อยลงหนึ่ง") Ed25519 ถูกเลื่อนออกไปตามเส้นทางการย้าย PQ ใน §15
deep hash ที่ทำมือเป็นโค้ดที่เสี่ยงสูงสุด มีการครอบคลุมตามธรรมชาติต่ำสุดใน W5 ดังนั้นการกั้นของมันจึง ต่อรองไม่ได้ (§16.15 Q8):
- fixture ข้ามภาษา
tlog_v1.json(Rust + TS รูปแบบwrapper_v1.jsonของ §14.5) ครอบคลุม deep-hash ไบต์ DataItem + id แฮชลีฟ ราก 5 ลีฟ + เส้นทางตรวจสอบ แฮช STH หลักฐานการรวม และหลักฐานความสอดคล้อง — ในทั้งทิศทางลงนามและทิศทางตรวจสอบ (ทิศทางตรวจสอบสำคัญเพราะการตรวจสอบ tx → tx_id ในเครื่องของ §16.6 ดึง deep hash เข้าสู่ตัวตรวจสอบยืนเดี่ยวทุกตัว ไม่ใช่เพียงผู้เขียน) - การไปกลับ interop ครั้งเดียวผ่าน bundler ANS-104 อ้างอิง บริโภคเป็น ข้อมูลทดสอบสถิตเท่านั้น — ไม่เคยเป็น dependency รันไทม์ npm (ท่าที Web-Crypto-เท่านั้น / ไม่มี install-script ยังคงอยู่)
- เส้นทาง deep-hash + RSA-PSS ต้องไปกลับผ่านองค์ประกอบพื้นฐาน
crypto.subtleเดียวกัน กับที่การผลิตใช้ ดังนั้นตัวเข้ารหัสภายในจึงเข้ากันได้ระดับไบต์ - มอนิเตอร์การยอมรับหลัง bundle ที่ดำเนินต่อเนื่อง ยืนยันว่าแต่ละ DataItem จุดยึด / ทางสำรอง บรรลุการยอมรับของ Arweave จริง พร้อมการเตือน + เบรกเกอร์ตัดวงจร — เพราะ deep hash ยังให้บริการคิวทางสำรองเมื่อ Arweave ไม่พร้อมใช้งานด้วย ดังนั้นการถดถอยแบบเงียบจะเติมคิวนั้นด้วยรายการที่เครือข่ายปฏิเสธในช่วงไฟดับที่มันมีไว้เพื่อครอบคลุมพอดี
16.9 หลักฐานการรวมและความสอดคล้อง
ทั้งสองเป็น RFC 9162, SHA3-256, ให้บริการเป็น CBOR มาตรฐาน
InclusionProof — GET /api/v1/qub/:tx_id/proof: ver:u8, leaf:bstr (CBOR ลีฟที่แม่นยำ — ตัวตรวจสอบคำนวณ leaf_hash ใหม่เองและไม่เคยเชื่อถือแฮชที่ให้มา), 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 รายการกุญแจเดียวที่ไม่กำกวม ตรึงด้วยเวกเตอร์ทดสอบ
การตรวจสอบแบบยืนเดี่ยว (ไม่มีเซิร์ฟเวอร์ qub ขยาย §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).
ที่จัดเก็บที่ให้บริการหลักฐานต้องคีย์ด้วยพิกัด (ได้ข้อสรุป — §16.15 Q7 เงื่อนไขเบื้องต้นที่ปิดกั้น) การสร้างหลักฐานลีฟเย็นเป็นกลางต่อความถูกต้อง ก็ต่อเมื่อ วัสดุตรวจสอบ R2 เป็น ที่จัดเก็บโหนดเมอร์เคิลถาวรที่คีย์ด้วยพิกัดต้นไม้สัมบูรณ์ (level, index) — ไม่ใช่เดลตาโหนดต่อ batch ด้วยที่จัดเก็บที่คีย์ด้วยพิกัด เส้นทางตรวจสอบ (leaf i, size N) ใด ๆ คือเซตของ R2 GET โดยตรง O(log N) ครั้งโดย ไม่มีการคำนวณใหม่ข้ามขอบเขตชุด; ด้วยที่จัดเก็บที่คีย์ด้วยชุดมันไม่เป็นเช่นนั้น ซึ่งคือช่องว่างเลย์เอาต์ที่จัดเก็บที่การแก้นี้ปิด body ลีฟก็สามารถระบุที่อยู่ด้วยเนื้อหาโดย seq เช่นกัน เวกเตอร์ทดสอบ W5 ต้องพิสูจน์ ลีฟเย็นยุค genesis เทียบกับรากที่ภายหลังกว่ามากโดยใช้เพียง R2 + Arweave โดยล้างที่จัดเก็บ LogDO ดังนั้นการอ้างความปลอดภัยของการเรียกคืนใน §16.13 จึงมีหลักฐานหนุนแทนที่จะถูกกล่าวอ้าง R2 GET ตามลำดับ O(log N) ครั้งอยู่ เฉพาะ บนปลายทางหลักฐานแบบอะซิงโครนัส — ไม่เคยบนเส้นทางผนึกร้อน (§16.10) หรือ cron ต่อ tick
16.10 การจัดลำดับการตอบรับ R2-First
ที่ถูกนำไปใช้ POST /api/v1/upload ลำดับคือ:
- ประตูครึ่งหน้าส่วนหน้า (auth, การตรวจสอบ, idempotency shard key) — ไม่เปลี่ยนแปลง
- สร้าง ทำเครื่องหมาย และลงชื่อในธุรกรรม Arweave บุคคลที่แน่นอนนี้ นี่มาจาก
tx_idในระดับท้องถิ่น แม้ว่าการสร้างธุรกรรมอาจดึงข้อมูลรางวัล/ข้อมูลเมตาแองเคอร์จากเกตเวย์ ความล้มเหลวในการเตรียมก็ยังทำให้คำขอล้มเหลวก่อนการยืนยัน - แบบซิงโครนัส เขียนชิ้นงานที่เลือกที่
qub-cache/<tx_id>และคงไว้ซึ่งบันทึกการสร้าง-การดำเนินการ/เอาต์บ็อกซ์ที่เสถียร เหล่านี้คือความทนทานและระดับการลองใหม่; ความล้มเหลวก่อนการตัดยอดจะส่งกลับ 503 - เมื่อ
LOG_DOถูกกำหนดค่าแล้ว, พยายามพร้อมกันLogDO.append(leaf). นักเขียนคนเดียวมอบหมายseq, ขยายโซ่รายการ และอัปเดตแนวหน้า RPC การต่อท้ายทำเพียงเท่านั้น; การปิดแบบเป็นชุดทำงานนอกเส้นทางบนสัญญาณเตือน ขณะนี้ความล้มเหลวของการขนส่ง/แอปพลิเคชันการต่อท้าย ล้มเหลวแบบอ่อนไหว: การตอบสนองยังสามารถประสบความสำเร็จได้แม้ปราศจากlog_seq,receipt, หรือanchor_status. แม้จะมีความเห็นเกี่ยวกับการนำไปใช้ แต่วันนี้ยังไม่มีการเชื่อมต่อการปรับสมดุลบันทึกอัตโนมัติในภายหลัง - ส่งการยืนยันคืน รวมถึง
{ log_seq, anchor_status: "pending", receipt }เฉพาะเมื่อ append ส่งค่ากลับ tuple ที่สำเร็จสมบูรณ์เท่านั้นreceipt.sig_b64urlว่างเปล่าเมื่อผู้ลงนามใบเสร็จไม่พร้อมใช้งาน; ลูกค้าไม่ควรเรียกค่านั้นว่าถูกลงนามหรือไม่สามารถปฏิเสธได้ การไม่มีทูเพิลหมายถึงการเผยแพร่อย่างถาวรเท่านั้น ไม่ใช่การยอมรับบันทึกความโปร่งใส - ใช้งานเลื่อนเดียวในการโพสต์ธุรกรรมที่ลงนามอย่างถูกต้อง ความสำเร็จจะลบกล่องส่ง; ความล้มเหลวจะทิ้งไว้สำหรับ cron การระบายที่จำกัดและต้องไม่เปลี่ยนสิ่งที่ได้รับการยืนยันแล้ว
tx_id. ข้อมูลเมทาดาต้าชั่วคราวและซ้ายข้างอื่น ๆ ที่พยายามอย่างดีที่สุดก็ถูกเลื่อนออกไปเช่นกัน
ขอบเขตความหน่วง เส้นทางการร้องขอรวมถึงงานอํานาจ/โควตาของครึ่งหน้าด้านหน้า การเตรียมการ/การลงนามธุรกรรม การเขียน R2 ที่ทนทาน และ (เมื่อมีการตั้งค่า) LogDO พยายาม < 300 ms ปรากฏในการตรวจสอบการออกแบบในฐานะเป้าหมายการดำเนินงาน ไม่ใช่การรับประกันตามโปรโตคอล; ขั้นตอนการเตรียมธุรกรรมในปัจจุบันอาจดำเนินการร้องขอข้อมูลเมตาของเกตเวย์ การเตือนความหน่วงเวลาและประตูการเปิดตัวเป็นการควบคุมการดำเนินงาน ไม่ใช่หลักฐานที่สามารถใช้ได้กับผู้ตรวจสอบ
16.11 แบบจำลองความเชื่อถือ — การอ้างที่แม่นยำ ขอบเขตตามชนิดลีฟ
สำหรับ kind=0x01 (รับรอง): "เนื้อหานี้ — body ตรงกับ body_hash ระบุด้วย qub_id — ถูกผูกพันเข้าบันทึกเพิ่มต่อท้ายเท่านั้นของ qub ที่ตำแหน่ง seq และมีอยู่ไม่ช้ากว่าเวลาบล็อก Arweave T; มันอ่านไม่ได้ในเชิงการเข้ารหัสจนถึงรอบ drand R = unlock_round(unlock_at)" นี่คือชุดสาม {การผูกรอบ tlock + การรวมเมอร์เคิล + รากที่ยึดแล้ว} เต็ม
สำหรับ kind=0x02 (กล่าวอ้าง ค่าเริ่มต้น): "ไซเฟอร์เท็กซ์ทึบแสงที่ที่อยู่เนื้อหา chash อ้าง qub_id และ unlock_at ถูกผูกพันเข้าบันทึกเพิ่มต่อท้ายเท่านั้นที่ตำแหน่ง seq และมีอยู่ไม่ช้ากว่าเวลาบล็อก Arweave T" ขารอบและ body มาจากการตรวจสอบ .qub-bundle §11 ที่มีอยู่ (qub_core::unlock) ไม่ใช่ จากบันทึก; สิ่งที่บันทึกเพิ่มเหนือธุรกรรมต่อ qub เปล่า ๆ คือการจัดลำดับที่ตรวจจับการดัดแปลงได้ เวลาข้อผูกพันขอบเขตบนที่ไม่ต้องเชื่อถือ และการต้านการให้ข้อมูลขัดแย้ง
การอ้างทั้งสองยกเว้น ตาม §11: ผู้เขียนโดยไม่มี sig_alg ≥ 0x01 เจตนา และเวลาความละเอียดต่ำกว่าจุดยึด ทั้งสองไม่ให้การอ้างใดพึ่งพา received_at
เพดานการเรียกร้อง (ข้อจำกัดการเปิดตัวที่ผูกพัน — แก้ไขแล้ว §16.15 Q1) สำหรับที่อ้างว่า (kind=0x02) ใบ, ข้อเรียกร้องที่ครอบคลุมข้างต้นคือ เพดาน เกี่ยวกับสิ่งที่ผลิตภัณฑ์ การตลาด เงื่อนไข หรือพื้นผิวการแสดงผลใด ๆ อาจระบุ ไม่อนุญาตให้พื้นผิวใดกล่าวหรือสื่อว่าข้อมูลบันทึกพิสูจน์เนื้อหาหรือรอบการปลดล็อกของการอัปโหลดแบบบอดไบต์ — ข้อมูลบันทึกพิสูจน์เพียง การจัดลำดับ + เวลาผูกมัดขอบบนโดยไม่ต้องไว้ใจของข้อความเข้ารหัสที่ไม่โปร่งใส การพิสูจน์เนื้อหาและรอบมาจาก §11 ที่มีอยู่เท่านั้น .qub- การตรวจสอบแพ็กเกจ ซึ่งไม่ขึ้นกับบันทึกการทำงาน การเผยแพร่ที่ไม่มีการแนบ/รับรองความสำเร็จ จะไม่มีข้อเรียกร้องบันทึกใด ๆ เลย
16.12 การกำหนดเวอร์ชันและการประสานงาน W3
มี ไม่ SealedQub ตุ่มสายไฟ และดังนั้น ไม่มีการเพิ่มเวอร์ชันของโปรโตคอล (§12.2): บันทึกเป็นไซด์คาร์ที่มุ่งมั่นไปยังฟิลด์และไบต์ที่มีอยู่ ดังนั้นจึงไม่เข้าสู่ประวัติรุ่นโปรโตคอล §12.3 ตัวเลือกของ W3 drand_chain_version ยังไม่ได้แตะต้องและยังคงเป็นตัวเลือกเดียว SealedQub สนาม แทนที่จะใช้บันทึก จะมีการแนะนำช่องว่างเวอร์ชันอิสระของตัวเอง — LOG_VERSION_1, ANCHOR_FORMAT_1, InclusionProof.ver — สะท้อนความเป็นอิสระของเวอร์ชันห่อหุ้ม §12.5 (ห่อหุ้มมีไบต์เวอร์ชันที่แยกจากเวอร์ชันโปรโตคอล และเวอร์ชันของบันทึกตามแยกเช่นเดียวกัน)
การส่งมอบหลักฐานถูกดึงโดยค่าเริ่มต้น โดยมีตัวพ่วงที่เลือกได้ หลักฐานไม่สามารถมีอยู่ที่เวลาผนึก (จุดยึดยังไม่ถูกเขียน) ดังนั้น .qub bundle เวลาผนึกจึงปลอดหลักฐาน ตัวตรวจสอบของ W7 ดึง GET …/proof ครั้งเดียว หรือในโหมดออฟไลน์เต็มสร้างหลักฐานใหม่จาก AnchorBundle สาธารณะผ่านการสอบถาม Arweave บน Log-Id .qub bundle (W7) สงวน สมาชิก inclusion_proof ที่เลือกได้ — ไม่มีที่ผนึก เติมโดยการส่งออกใหม่หลังการยึดสำหรับการเก็บถาวรเย็น — ตามรูปแบบ "เลือกได้ ละเว้นโดยค่าเริ่มต้น เพิ่มเติม" เดียวกันกับ drand_chain_version ของ W3
16.13 การเก็บรักษา
หน้าต่างการเก็บรักษาสำหรับหางเปิดของ LogDO สารตั้งต้นที่ให้บริการหลักฐาน R2 ตัวนับเบรกเกอร์ตัดวงจรจุดยึด และคิวทางสำรองของ bundler ถูกระบุใน docs/DATA-RETENTION.md หลักการ: ที่จัดเก็บต่อรายการร้อนของบันทึก (LogDO) เรียกคืนได้หลังการยึด; วัสดุตรวจสอบของมัน — ที่จัดเก็บโหนดเมอร์เคิลที่คีย์ด้วยพิกัด (level, index) + body ลีฟที่ระบุด้วย seq (§16.9) + จุดยึด Arweave — เป็นแบบถาวร การเรียกคืนลีฟเย็นจาก DO ไม่เคยทำให้หลักฐานที่ออกแล้วเป็นโมฆะ เพราะหลักฐานแก้ไขเทียบกับที่จัดเก็บโหนด R2 ถาวรนั้นและจุดยึด Arweave ไม่ใช่ DO (และเวกเตอร์ทดสอบ DO ที่ถูกล้างของ §16.9 พิสูจน์มัน)
16.14 เวกเตอร์ทดสอบ
W5 จัดส่ง fixture ข้ามภาษา tlog_v1.json (§16.8) บวกเวกเตอร์ที่ทำงานแล้ว: ลีฟ kind=0x01 และ kind=0x02 → leaf_hash; รากสะสม 5 ลีฟ; หลักฐานการรวมหนึ่ง; หลักฐานความสอดคล้องหนึ่ง; AnchorBundle หนึ่ง; และ id DataItem หนึ่ง สิ่งเหล่านี้อยู่ควบคู่กับเวกเตอร์ตัวห่อหุ้มภายนอกของ §14.5 และถูกใช้งานโดยทั้งการใช้งาน Rust (qub-core) และ TypeScript (Worker)
16.15 ทบทวนการตัดสินใจ (W5 — ได้รับการแก้ไข)
การตรวจสอบภายนอก W5 (การออกแบบเชิงโต้แย้ง + การอนุมัติจากเจ้าของ) เสร็จสมบูรณ์แล้ว การตัดสินใจแต่ละข้อด้านล่างได้รับการตัดสินและสะท้อนในข้อความ §16 ข้างต้น; ข้อจำกัดในการเปิดตัวที่ผูกมัด จะถูกกล่าวซ้ำที่ท้าย การปฏิบัติสามารถดำเนินการภายใต้พวกมันได้
- เส้นทางเริ่มต้น (
kind=0x02) ใบความซื่อสัตย์ — ได้ข้อสรุปแล้ว. จัดส่งแบบแยกสองใบตามที่ระบุ:kind=0x02ไม่มุ่งมั่นทั้งสองฝ่ายbody_hashก็ไม่drand_round. ไม่*_body_hashสนามบนเส้นทางที่ตาบอดต่อไบต์ (มันจะเป็นสัญญาณ "ยืนยัน" ที่ปลอมแต่ชัดเจนที่สุดสำหรับผู้รวมและเป็นความสะดวกที่มาตรา 11 มีให้จากแพ็กเกจ) ทำ ไม่ ต้องการการประทับตราเซิร์ฟเวอร์สำหรับ qubs ที่รับรองโดยบันทึก (ซึ่งจะบังคับให้ข้อความธรรมดาผ่าน Worker และทำลายคูเมืองการทำลายล้างทางคริปโต) ส่วนวงจรสั้นที่บรรยายตัวเองใด ๆ ควรอยู่ใน.qubซองเอกสารรวม / พิสูจน์เอกสารเป็นฟิลด์ที่ตรวจสอบซ้ำโดยผู้ตรวจสอบ ไม่เคยเป็นฟิลด์ใบไม้ ขีดจำกัดการเรียกร้องที่ยืนยันโดยเจ้าของ: §16.11 - ความรับผิดชอบด้านการพูดกำกวมหรือการละเว้น — การออกแบบแก้ไขแล้ว การจัดเตรียมไม่สมบูรณ์ การออกแบบต้องการให้กุญแจรับประทับตราถูกปักไว้
LogProfileและลงนามข้ามanchor_owner, รวมถึงวิธีการตรวจสอบ, การเดินตามห่วงโซ่ก่อนหน้า, และหัวข้อสองส่วนที่ตีพิมพ์ด้วยตนเอง โปรไฟล์ที่คอมไพล์และฮุคสำหรับการปรับใช้ยังคงเป็นเพียงตัวแทน/ไม่บังคับตามที่ระบุใน §16.6 ดังนั้นข้อเรียกร้องที่แข็งแกร่งกว่า ตรวจพบ + ได้รับใบเสร็จ จะไม่เป็นปัจจุบันจนกว่าประตูเหล่านั้นจะปิด ต้องไม่ทำการตลาดว่าเป็น พยานอิสระ จริง พยานบุคคลที่สามจริงจะถูกเลื่อนออกไปในการปรับปรุงการกำกับดูแล §15 - รากความน่าเชื่อถือของเจ้าของสมอที่ตรึงไว้ + การหมุน — แก้ไขแล้ว. รับเลี้ยง
LogProfileพิน (§16.6); ผู้ตรวจสอบจะตรวจสอบanchor_tx.owner == anchor_ownerและยืนยันข้อมูล tx → การผูก tx_id แบบท้องถิ่น การบริหารการหมุนเวียนคือ §15 ส่วนขยายสำหรับสร้าง (§15.3 การกระตุ้นถูกเพิ่ม), ไม่ใช่การนำกลับมาใช้ใหม่; การหมุนเวียนที่วางแผนไว้ข้ามสัญลักษณ์ การหมุนเวียนที่ขับเคลื่อนด้วยการประนีประนอมจะกลับไปยังการชน §15 พร้อมกับการตรวจสอบส้อมเพื่อจำกัดความเสียหาย. - การทำให้ใบ Private-qub ตาบอด — ได้รับการแก้ไขแล้ว เก็บการทำให้ตาพร่าไว้สำหรับคิวบ์ส่วนตัว (
ref = SHA3-256(qub_id ‖ log_blind_secret)), ดิบqub_idสำหรับ qubs สาธารณะ (มีอยู่แล้ว §16.2.1),chashในฐานะสายสัมพันธ์อิสระlog_blind_secretเป็นความลับระดับความสัมพันธ์/ซิบอล หมุนไปข้างหน้าเท่านั้น (§16.2.1) received_at— ได้ข้อสรุปแล้ว. เก็บมันไว้ในใบไม้ มุ่งมั่นแต่ชัดเจนว่าไม่ใช่หลักฐาน; ไม่เคยปรากฏเป็นหลักฐานหรือการยืนยันข้อพิพาทในทุกพื้นผิว การตรวจสอบความสมเหตุสมผลของมอนิเตอร์ใด ๆ จะเปรียบเทียบกับเวลาบล็อกของ ArweaveT, ไม่ใช่ที่ควบคุมโดยผู้ปฏิบัติการanchored_at(§16.6)- การพิสูจน์เวลาแบบเป็นชั้น — การแก้ไขการออกแบบ ไม่ใช่การวางเส้นทางปัจจุบัน การออกแบบที่ได้รับการตรวจสอบมอบเวลาบล็อกยึดให้กับชั้นที่จัดกลุ่ม และการพิสูจน์ชั่วโมงที่แน่นอนให้กับ T3 ที่ชำระเงิน โดยไม่มี SLA ตัวเลขสำหรับอย่างแรก เส้นทางปัจจุบันยังไม่ได้กำหนดความแตกต่างทางการค้านั้น: พวกเขานัดหมายธุรกรรมแต่ละรายการสำหรับการตีพิมพ์ที่ยอมรับ และการบันทึกความครอบคลุมยังคงมีเงื่อนไขดังที่ระบุใน §16.1/§16.10 ข้อความผลิตภัณฑ์ต้องอธิบายการใช้งาน ไม่ใช่การแบ่งชั้นในอนาคตนี้
- ต้นสะสมเกี่ยวกับคนงาน — แก้ไขเรียบร้อยแล้ว ต้นไม้ RFC 9162 รวมเดี่ยว + LogDO ผู้เขียนเดี่ยวที่เก็บหน้าแรกแบบแคช (มีพื้นที่ว่างสบายเมื่อเทียบกับเพดาน DO ประมาณ 1k การเขียนต่อวินาที; เลื่อนการแบ่ง Merkle-of-shard-roots จนใกล้ถึงเพดานนั้น) ที่มีคีย์การประสานงาน
(level, index)โหนดเก็บ R2 + ตัวเวกเตอร์ทดสอบใบเย็น wiped-DO ถูกนำไปใช้งาน (§16.9)< 300 msยังคงเป็นเป้าหมายด้านการออกแบบ/การปฏิบัติการ ไม่ใช่คำสัญญาของโปรโตคอล (§16.10) - โครงการลายเซ็น ANS-104 + แฮชเชิงลึก — แก้ไขแล้ว. RSA-PSS (ประเภทลายเซ็น 1 ใช้ JWK ของกระเป๋าแองเคอร์เฉพาะซ้ำได้); Ed25519 เลื่อนเป็นเส้นทาง PQ §15 แฮชเชิงลึก SHA-384 ที่ทำด้วยมือถูกจำกัดบนฟิกเจอร์ cross-impl ทิศทางทั้งสอง, การตรวจสอบการทำงานร่วมของ reference-bundler แบบสแตติกเท่านั้น, การแชร์-
crypto.subtleไป-กลับ และตัวตรวจสอบการยอมรับ Arweave หลังจากการรวมแพ็กเกจ (§16.8)
ข้อจำกัดในการเปิดตัวแบบผูกมัด (นำไปสู่การดำเนินการ + การตรวจสอบผลิตภัณฑ์/กฎหมาย):
- เพดานการเรียกร้อง (Q1/Q6) ไม่มีพื้นผิวใดสามารถกล่าวได้ว่า ท่อนซุงพิสูจน์ เนื้อหาของใบที่อ้างอิงหรือปลดล็อกรอบ; ข้อเรียกร้องที่อนุญาตสำหรับสิ่งที่ยึดได้สำเร็จ
kind=0x02ใบไม้ถูก จัดเรียงอย่างมีลำดับ, ตรวจสอบการถูกงัดแงะได้, ด้วยเวลามัดจำขีดจำกัดบนที่ไม่ต้องเชื่อใจ การตอบกลับที่ไม่มีคู่ใบเสร็จไม่มีข้อเรียกร้องบันทึก ไม่มีสำเนาเวลากระจายใดที่ให้การรับประกันความหน่วงเชิงตัวเลข - เป็นพยานถึงความซื่อสัตย์ (ข้อ 2) ความกำกวมของตลาดในฐานะ ตรวจจับได้ + มีใบเสร็จรับเงิน ไม่เคย มีพยานอิสระ
- ใบเสร็จ + กุญแจสมอ (Q2/Q8) ก่อนที่เอกสารสิทธิ์การไม่ปฏิเสธ/การยืนยันที่ยึดตรึงจะถูกจัดส่ง ให้จัดเตรียมและคอมไพล์พินคีย์สาธารณะของใบเสร็จ ลงนามข้ามกับเจ้าของแองเคอร์ที่จัดเตรียมไว้ และเก็บกระเป๋าเงินแองเคอร์เป็น JWK ของตัวเองที่แยกจากกระเป๋าเงินอัปโหลด
- เกตแฮชลึก (Q8) ไม่มีการส่งมอบเรือจอดสมอหรือเรือ T3 จนกว่าจะผ่านการตรวจสอบการติดตั้งสองทิศทางและการทำงานร่วมกัน; เครื่องมือตรวจสอบการยอมรับจะแจ้งเตือนเมื่อพบความล้มเหลว
- เงื่อนไขการจัดเก็บล่วงหน้า (Q7). การจัดเก็บโหนดแบบระบุพิกัด + เวกเตอร์ใบเย็น DO ที่ถูกลบเป็นเงื่อนไขเบื้องต้นสำหรับการรับประกันว่า 'การกู้คืนจะไม่ทำให้หลักฐานเป็นโมฆะ'
17. ชุดตรวจสอบแบบพกพา (.qub)
สถานะ ส่วนนี้คือ ดำเนินการ (W7 / UP-C2):
qub_core::exportสร้างและวิเคราะห์แพ็กเกจ และtools/qub-verifyเป็น CLI สาธารณะแบบครบวงจรที่ตรวจสอบแบบออฟไลน์ §11 และ §16.9 อ้างอิงถึง "the" แล้ว.qub"bundle" เป็นหน่วยที่ตัวตรวจสอบอิสระใช้; ส่วนนี้ระบุขนาดเป็นไบต์และขั้นตอนการตรวจสอบอย่างละเอียด มันเป็นแบบเพิ่มเท่านั้น — bundle จะจัดชุดข้อมูลอินพุต §11 ที่มีอยู่แล้วและไม่เปลี่ยนรูปแบบข้อมูลบนเชนใด ๆ
17.1 วัตถุประสงค์
§11 กำหนดว่าบุคคลที่สามใด ๆ สามารถตรวจสอบสิ่งประดิษฐ์ทางการเข้ารหัสของ qub ได้โดยไม่ต้องมีความร่วมมือจาก qub .qub แพ็กเกจนั้นทำการยืนยัน พกพาได้และใช้งานแบบออฟไลน์: มันบรรจุ CBOR ที่ถูกปิดผนึกและลายเซ็นรอบ drand ที่ปลดล็อกมันเข้าด้วยกันเป็นสิ่งประดิษฐ์ที่มีตัวเองครบถ้วน ดังนั้นผู้รับสามารถยืนยันความสมบูรณ์ของเนื้อหา การผูกมัดรอบ และลายเซ็นผู้ประพันธ์ใด ๆ กับ ไม่มีการโทรเครือข่ายเลย (ไม่มีการดึงข้อมูลจากการจัดเก็บ, ไม่มีการร้องขอ drand สด, ไม่มี API ของ qub) แพ็กเกจเพียงอย่างเดียวไม่สามารถพิสูจน์ได้ว่า ciphertext ถูกสร้างขึ้นเมื่อใด; การทำธุรกรรมการจัดเก็บที่ตรวจสอบได้อย่างอิสระหรือหลักฐาน log ที่ยึดแน่น จะให้คำยืนยันเวลาการมีอยู่ที่แยกออกมา (§11, §17.5)
17.2 รูปแบบแพ็กเกจ
เอ QubBundle เป็น CBOR มาตรฐานที่เขียนด้วยมือภายใต้โปรไฟล์ §3.1 (ความยาวแน่นอน ไม่มีแท็ก ไม่มีตัวเลขทศนิยม จำนวนเต็มรูปแบบสั้นที่สุด ข้อความในรูปแบบ NFC ช่องว่างที่ไม่จำเป็นถูกละไว้เมื่อไม่มี การจัดลำดับคีย์ตามความยาวไบต์ที่เข้ารหัสจากน้อยไปมาก จากนั้นตามลำดับไบต์) การจัดลำดับคีย์ทั้งสามที่มี 15 ตัวอักษร d < i < s. ดิบ .qub ไฟล์คือไบต์เหล่านี้อย่างแน่นอน; สำหรับการส่งผ่าน URL หรือการคัดลอก-วาง ไบต์เดียวกันถูกเข้ารหัสเป็น base64url (ไม่มีตัวเติม)
| กุญแจ | ความยาวของเอกสารแนบ | ประเภท | การปรากฏตัว | ความหมาย |
|---|---|---|---|---|
version |
แปด | u8 |
จำเป็น | เวอร์ชันรูปแบบแพ็กเกจ (0x01). |
sealed_at |
สิบ | i64 |
ตัวเลือก | เวลาประทับตราที่ผู้สร้างยืนยัน (วินาที Unix); อธิบายตัวเอง, ไม่ใช่หลักฐาน |
drand_round |
สิบสอง | u64 |
จำเป็น | รอบ ๆ คิวบ์ถูกล็อกไว้ การฉายภาพของคิวบ์ที่ถูกฝังและปิดผนึก |
arweave_tx_id |
สิบสี่ | tstr |
จำเป็น | รหัสธุรกรรมที่บันทึกไบต์ที่ปิดผนึกไว้ (ตัวชี้แหล่งกำเนิด) |
drand_chain_id |
สิบห้า | tstr |
จำเป็น | โซ่ drand (ฐานสิบหก) การฉายภาพของ qub ที่ฝังและปิดผนึก |
drand_signature |
สิบหก | bstr |
จำเป็น | ลายเซ็นของเสาส่งสัญญาณ drand สำหรับ drand_round — ค่าที่ใช้ปลดล็อกข้อความที่เข้ารหัส |
inclusion_proof |
สิบหก | bstr |
ตัวเลือก | หลักฐานการรวม Merkle ของบันทึกความโปร่งใส §16 หลังจากที่มีหลักฐานยืนยันแล้ว (§17.5) |
sealed_qub_cbor |
สิบหก | bstr |
จำเป็น | ด้านใน SealedQubCbor ไบต์ (หลังจาก §13-แกะออก), คืออินพุตการตรวจสอบ §11 |
drand_round และ drand_chain_id เป็นการประมาณการที่สะดวกของ sealed_qub_cbor, ถูกจัดเก็บเพื่อให้เครื่องมือสามารถอ่านได้โดยไม่ต้องวิเคราะห์ CBOR ภายใน มันถูกสร้างขึ้นในระหว่างการสร้างและ ตรวจสอบการถอดรหัสอีกครั้ง ต่อต้าน qub ที่ถูกแยกวิเคราะห์และปิดผนึก; แพ็กเกจที่ฟิลด์ระดับบนสุดของมันไม่ตรงกับข้อมูลที่บรรจุจะถูกปฏิเสธ วินัยของตัวเข้ารหัสสะท้อนรูปแบบสัญญาณส่วนที่เหลือ: ปฏิเสธค่าที่ว่าง drand_signature หรือ arweave_tx_id, และผูกทุกฟิลด์ที่มีความยาวตัวแปร
17.3 สิ่งที่ลายเซ็น drand ที่ฝังไว้พิสูจน์
ชุดนี้มาพร้อมกับลายเซ็นของ drand แทนที่จะต้องให้ผู้ตรวจสอบไปดึงมา การถอดรหัสแบบ timelock (tlock บนเชนของ drand, §8) สามารถทำสำเร็จได้เฉพาะกับ แท้จริง ลายเซ็นไฟสัญญาณสำหรับรอบที่ผูกมัด—ค่าที่เชนเผยแพร่เพียงครั้งเดียวเมื่อรอบนั้นสิ้นสุดลง และซึ่งเป็นลายเซ็น BLS ที่ถูกต้องภายใต้กุญแจสาธารณะของเชน ลายเซ็นที่ปลอมหรือผิดพลาดจะล้มเหลวในการตรวจสอบ BLS หรือการถอดรหัส IBE/AEAD ดังนั้นชุดข้อมูลที่ถอดรหัสได้จึงพิสูจน์ว่า: ข้อความเข้ารหัสถูกผูกกับรอบ R และรอบ R ได้ผ่านไปแล้ว. ตัวตรวจสอบปักโซ่ (DrandTimelockProvider::quicknet()) และใช้การตรวจสอบการผูกรอบ §11 ดังนั้นชุดข้อมูลไม่สามารถอ้างสิทธิ์รอบที่ข้อความรหัสของมันไม่ถูกผูกไว้กับได้
นี่คือหลักฐานเงื่อนไขการปล่อย ไม่ใช่เวลาสร้าง หลังจากรอบ R
ผ่านไปแล้ว ใครก็สามารถสร้างรหัสลับใหม่สำหรับ R และจัดแพ็กเกจของมันที่เปิดเผยอยู่แล้ว
ลายเซ็น ดังนั้นเพียงแค่ชุดเอกสารนั้นจะต้องไม่ถูกอธิบายว่าเป็นหลักฐานที่
ข้อความเข้ารหัสหรือเนื้อหาที่มีอยู่ก่อน R ก่อน unlock_at, หรือก่อนเหตุการณ์ใด ๆ
17.4 การตรวจสอบแบบออฟไลน์แบบทีละขั้นตอน
qub-verify <file.qub> เรียกใช้ขั้นตอนมาตรฐาน §11 ทั้งหมดจากแฟ้มรวม ขับเคลื่อน qub_core::unlock::unlock พร้อมกับที่ปักหมุด DrandTimelockProvider:
1. Parse the .qub bytes → QubBundle (canonical-CBOR guard; bound every field;
re-check drand_round / drand_chain_id against the embedded sealed qub).
2. BLS-verify bundle.drand_signature for the pinned chain and round, then
tlock_decrypt(sealed.tlock_ciphertext, bundle.drand_signature) → QubEnvelope.
3. Verify SHA3-256(body) == body_hash (§11 step 8).
4. Verify QubEnvelope.qub_id == SealedQub.qub_id (§11 step 9).
5. Verify QubEnvelope.unlock_at == SealedQub.unlock_at (§11 step 10).
6. Verify ciphertext round == unlock_round(unlock_at) and the chain binding.
7. If sig_alg != 0x00: verify author_signature (and any cosigner; §9.4).
8. Report integrity, round-elapsed/round-binding, authorship, and cosigner
verdicts separately, plus the recovered body. Do not report a commitment
timestamp unless step 9 succeeds.
9. Optional existence-time leg: verify an included §16 proof through its pinned
anchor, or independently verify the referenced storage transaction. Report
its block time as an upper bound on ciphertext existence.
CLI ปิดตัว 0 (ได้รับการยืนยัน), 1 (การตรวจสอบล้มเหลว — ยังคงล็อก อยู่, รหัสแฮชตัวเนื้อหาไม่ตรงกัน, การผูกรอบ/โซ่เสียหาย, หรือ ลายเซ็นที่ตรวจสอบไม่ผ่าน), หรือ 2 (การใช้งาน / แพ็กเกจที่ไม่ถูกต้อง). A --json รายงานมีคำตัดสินเดียวกันสำหรับการทำงานอัตโนมัติ เนื่องจากชุดแพ็กเกจเป็นแบบรวมตัวเอง ทำให้กล่องตรวจสอบ (qub-core) และ CLI (qub-verify) เป็นซอฟต์แวร์เดียวที่บุคคลที่สามต้องการ; ทั้งสองเป็นสาธารณะและใช้เส้นทางการตรวจสอบที่มีอยู่ของโปรโตคอล — ไม่มีการเข้ารหัสที่สร้างขึ้นเฉพาะ
17.5 ความสัมพันธ์กับบันทึกความโปร่งใส
inclusion_proof เป็นช่องทางเสริมสำหรับการพิสูจน์การรวมของ §16 Merkle การตรวจสอบเฉพาะ bundle (§17.4) สมบูรณ์สำหรับ ความซื่อสัตย์ การผูกแบบกลม / รอบที่ผ่านไป และการมีชื่อผู้แต่งแบบเลือกได้แต่จงใจไม่มีการอ้างสิทธิ์การมีอยู่ที่มีเวลาประทับอิสระใด ๆ ที่ตรวจสอบแล้วอย่างสมบูรณ์และมีประชากรเต็ม inclusion_proof เพิ่มพันธะเฉพาะชนิดใบและเวลาขอบบนจาก §16.11 โดยไม่เปลี่ยนเวอร์ชันรูปแบบชุดหลักฐาน การไม่มีหลักฐานหมายถึงเพียง "ไม่มีหลักฐานรวมอยู่"—ไม่ใช่ "ไม่ถูกต้อง" และไม่จำเป็นต้องหมายถึง "ไม่ได้ถูกยึด"
ในการใช้งานอ้างอิง ช่องตอนนี้คือ พิมพ์: qub_core::export::QubBundle::inclusion_proof_typed() ส่งคืน Option<InclusionProof> ถือโครงสร้าง §16.9 แบบเต็ม (ใบ, เส้นทางตรวจสอบ, รากที่ยึดแน่น, และ AnchorRef) ผ่านฟิลด์ CBOR ทึบเดียวกัน — ไม่มีการปรับเวอร์ชันของรูปแบบบันเดิล แยกตัว qub-verify CLI ใช้มันผ่าน --anchor ขา และ — จนกว่าแอนเคอร์วอลเล็ทจะถูกจัดเตรียม (§16, สถานะ) — รายงานหลักฐานเจ้าของที่ถูกเติมเต็มแต่เป็นเพียงตัวแทนเป็น เฉพาะการรวม แทนที่จะเป็น ยืนยันการยึดครบถ้วน