ข้อกำหนดโปรโตคอล 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), ดังนั้นเวลาที่ปลดล็อกที่แสดงจึงสามารถพิสูจน์ได้ว่าเป็นรอบที่เปิดการถอดรหัส

คุณสมบัติ:

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) ต้องบังคับใช้:

  1. HTTPS เท่านั้น สตริงต้องขึ้นต้นด้วยลำดับไบต์ https:// สคีมาอื่นใด — http, ftp, javascript, data, file ฯลฯ — จะถูกปฏิเสธ
  2. เพดานความยาว ≤ 2,048 ไบต์ (ขีดจำกัด URL เชิงปฏิบัติของเบราว์เซอร์)
  3. ตรวจ NFC + จุดรหัสที่เป็นอันตราย กฎเดียวกับ title และ reflection — จุดรหัส bidi-override / zero-width / tag-block / BOM / C0 / C1 จะถูกปฏิเสธ คำจำกัดความตรงกับ Rust crate::handle::contains_hostile_text_codepoint และ TS workers/api/src/utils/unicode.ts::isHostileCodepoint (รักษาให้สอดคล้องกัน)
  4. ไม่มีช่องว่าง ไม่มี ASCII control ช่องว่าง / DEL / ไบต์ต่ำกว่า 0x20 ที่ใดก็ตามใน URL จะถูกปฏิเสธ — ปิดช่องโหว่การฉีด \n/\t ที่กฎ bidi ไม่ครอบคลุม
  5. ส่วน 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 จึงเป็นพรีอิมเมจเดียวที่ยอมรับ

การใช้งานที่แสดง 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) ชั้นลายเซ็นที่สองพิสูจน์ว่าทั้งสองฝ่ายยินยอมตามเงื่อนไขเดียวกัน

ฟิลด์ซอง:

ทั้งสองฟิลด์ต้องอยู่ร่วมกันหรือไม่อยู่ร่วมกัน หากมีฟิลด์เดียวอยู่ ผู้ชมต้องรายงานข้อผิดพลาดความสมบูรณ์

ขั้นตอนการตรวจสอบ:

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."

คุณสมบัติ:

เกตการผูกอีเมล (การดำเนินงาน) เมื่อสัญญาที่จัดเตรียมไว้มีผู้ติดต่ออีเมลฝ่าย 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 องค์ประกอบที่อนุญาต

10.2 องค์ประกอบที่ห้าม

องค์ประกอบ การจัดการ
HTML ดิบ (<div>, <script> เป็นต้น) ตัดออกทั้งหมด ไม่มี HTML ผ่านได้
รูปภาพ (![alt](url)) ตัดออก ไวยากรณ์รูปภาพถูกลบออกจากเอาต์พุต
ลิงก์ ([text](url)) URL เรนเดอร์เป็นข้อความธรรมดาที่มองเห็นได้ ไม่ลิงก์อัตโนมัติ คลิกไม่ได้โดยไม่มีการกระทำที่ชัดเจนของผู้ใช้
รูปแบบ URL ที่อันตราย javascript:, data:, vbscript:, file: — ตัดออก
Iframe, embed, object ตัดออก
HTML entity ถอดรหัสเป็นอักขระแสดงผลเฉพาะเมื่อปลอดภัย

10.3 การใช้งาน

การใช้งานต้องใช้ ตัวแยกวิเคราะห์ allowlist ที่เข้มงวด ไม่ใช่ blocklist แนวทางที่แนะนำ:

  1. แยกวิเคราะห์ Markdown โดยใช้ pulldown-cmark (หรือเทียบเท่า)
  2. เดิน AST และตัดโหนดใด ๆ ที่ไม่อยู่ใน allowlist (§10.1)
  3. สำหรับโหนดลิงก์: ส่ง URL เป็นข้อความที่มองเห็นได้ ไม่ใช่เป็นองค์ประกอบ <a> ที่คลิกได้
  4. แปลง AST ที่กรองแล้วเป็น การแทนกลางที่กำหนดประเภท (เช่น enum MarkdownNode ที่มีเฉพาะตัวแปรที่ปลอดภัย) HTML ดิบไม่สามารถแทนได้ในเชิงโครงสร้างใน IR นี้
  5. เรนเดอร์จาก IR ที่กำหนดประเภทไปยังชั้นมุมมองเป้าหมาย (เช่น คอมโพเนนต์มุมมองแบบรีแอกทีฟ โหนด DOM) ไม่มีการต่อสตริง HTML หรือ innerHTML ในจุดใดก็ตาม

แนวทาง blocklist เปราะบางเนื่องจากส่วนขยาย Markdown ใหม่หรือลักษณะเฉพาะของตัวแยกวิเคราะห์อาจแนะนำองค์ประกอบที่ไม่ได้กรอง แนวทาง AST ที่กำหนดประเภททำให้ XSS เป็นไปไม่ได้ในเชิงโครงสร้าง — ไม่มีตัวแปรใดที่สามารถถือ HTML ตามอำเภอใจได้

10.4 ข้อจำกัดด้านขนาดและโครงสร้าง


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 ระบุเวอร์ชันโปรโตคอลหลัก

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)

ผลสุทธิ:

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
}

ค่าคงที่ของฟิลด์

การเข้ารหัส 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 การใช้งานอ้างอิงดึงจาก:

การแจกจ่าย: 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 นอกขอบเขตของส่วนนี้

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 อย่างชัดเจน


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 เดียวกัน:

อุปกรณ์ติดตั้งปัจจุบันยึดสามกรณีตัวหุ้มระดับต่ำ พวกมันทดสอบความแน่นอน 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 ผูกอัลกอริทึมหนึ่งตัวต่อหนึ่งองค์ประกอบพื้นฐานพอดี:

ตัวตรวจสอบปัจจุบันตั้งค่าความยาวของกุญแจและลายเซ็นต์ไว้แบบตายตัวตามพร้ามิทิฟที่ใช้งานอยู่ sig_alg และไบต์ของ wrapper-version เป็นตัวเลือกที่ชัดเจน แต่ v1 ไม่ทำการต่อรองในแถบสัญญาณและยอมรับเฉพาะค่าที่ใช้งานอยู่ด้านบนเท่านั้น

15.2 รูปร่างที่ตั้งใจ

เมื่ออัลกอริทึมที่สองเข้าสู่โปรโตคอล ตัวตรวจสอบจะถูกกำหนดค่าสำหรับ CryptoProfile ที่มีชื่อ (เช่น ExqubV1) ที่แสดงรายชุดค่าที่อนุญาตที่แม่นยำต่อองค์ประกอบพื้นฐาน — sig_algs, drand chain, เวอร์ชันตัวห่อหุ้ม, ประเภทเนื้อหา โปรไฟล์ถูกแก้ไขที่เวลาตรวจสอบ ไม่เคยถูกเจรจาในแบนด์ ค่าใดก็ตามที่อยู่นอกโปรไฟล์ที่ใช้งานจะถูกปฏิเสธ

สิ่งนี้รับประกันว่าการเพิ่ม ML-DSA-87 หรือการเปิดใช้งาน Ed25519 ไม่สามารถลดความเข้มแข็งของการตั้งค่าตัวตรวจสอบที่มีอยู่ย้อนหลังได้: ตัวตรวจสอบ v1 ยังคงเป็นตัวตรวจสอบ v1 แม้หลังจากที่โปรไฟล์ v2 ถูกเผยแพร่

15.3 เงื่อนไขการกระตุ้น

ยกระดับ §15 เป็นสถานะเชิงบรรทัดฐานเมื่อมีการเสนอข้อใดข้อหนึ่งต่อไปนี้:

จนกว่าจะถึงเวลานั้น §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)

  1. ใบเสร็จประทับตรา (ขึ้นอยู่กับการจัดหา) — อนาล็อก SCT ถูกส่งคืนเมื่อการแนบบันทึกของการอัปโหลดสำเร็จ (§16.10) มันจะไม่สามารถปฏิเสธได้ก็ต่อเมื่อ sig_b64url ไม่ว่าง และ ความสัมพันธ์ระหว่างกุญแจสาธารณะที่ตรงกัน/เจ้าของสมอถูกตรึงในผู้ตรวจสอบ พินโปรไฟล์การผลิตที่ว่างอยู่ในปัจจุบันไม่สามารถสนับสนุนคำตัดสินนั้นได้ การควบคุมนี้จะไม่ใช้กับชุดใบเสร็จที่ละเว้นหรือใบเสร็จที่ไม่ได้ลงนาม
  2. วิธีการตรวจสอบที่เผยแพร่ + การเดินตามโซ่ก่อนหน้า — สมอ prev โซ่ถูกเดินจากหัว→กำเนิด; ส้อม (สมอสองอันที่หนึ่ง) size ด้วยความแตกต่าง root, หรือชำรุด prev) เป็นหลักฐานที่สามารถตีพิมพ์ได้ของพฤติกรรมไม่เหมาะสม การตรวจจับการเลี่ยงคำพูดเป็นความมุ่งมั่นในการปฏิบัติที่ได้ระบุไว้ ไม่ใช่สมมติฐานเงียบ
  3. หัวที่ตีพิมพ์ด้วยตัวเองสองหัว — แต่ละหัวใหม่ {sth_hash, tree_size} ถูกโพสต์ไปยังที่ที่เฉพาะเจาะจงซึ่งเป็นของ qub ที่เก็บ GitHub สาธารณะ แบบเพิ่มได้อย่างเดียว (ขาตีพิมพ์ด้วยตนเองที่รับน้ำหนักได้และสามารถตรวจสอบการปลอมแปลงได้), โดยมีโพสต์โซเชียลเป็นเพียงการยืนยันตามความพยายามที่ดีที่สุดเท่านั้น การโพสต์ที่ล้มเหลวจะต้องมีการแจ้งเตือน (ไม่สามารถล้มเหลวโดยเงียบ) ดำเนินการแล้ว (ขั้นที่ 8) เป็น publishHead เกี่ยวกับสมอ cron (workers/api/src/utils/heads-publish.ts): a PUT ไปยัง API ของเนื้อหาโดยไม่มี sha เป็นแบบเพิ่มอย่างเดียว (a 422 หมายความว่าหัวข้อได้ถูกเผยแพร่แล้ว ไม่ใช่การเขียนทับ); การเลือกเข้าใช้งาน / ถูกจำกัดการปรับใช้ 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):

  1. fixture ข้ามภาษา tlog_v1.json (Rust + TS รูปแบบ wrapper_v1.json ของ §14.5) ครอบคลุม deep-hash ไบต์ DataItem + id แฮชลีฟ ราก 5 ลีฟ + เส้นทางตรวจสอบ แฮช STH หลักฐานการรวม และหลักฐานความสอดคล้อง — ในทั้งทิศทางลงนามและทิศทางตรวจสอบ (ทิศทางตรวจสอบสำคัญเพราะการตรวจสอบ tx → tx_id ในเครื่องของ §16.6 ดึง deep hash เข้าสู่ตัวตรวจสอบยืนเดี่ยวทุกตัว ไม่ใช่เพียงผู้เขียน)
  2. การไปกลับ interop ครั้งเดียวผ่าน bundler ANS-104 อ้างอิง บริโภคเป็น ข้อมูลทดสอบสถิตเท่านั้น — ไม่เคยเป็น dependency รันไทม์ npm (ท่าที Web-Crypto-เท่านั้น / ไม่มี install-script ยังคงอยู่)
  3. เส้นทาง deep-hash + RSA-PSS ต้องไปกลับผ่านองค์ประกอบพื้นฐาน crypto.subtle เดียวกัน กับที่การผลิตใช้ ดังนั้นตัวเข้ารหัสภายในจึงเข้ากันได้ระดับไบต์
  4. มอนิเตอร์การยอมรับหลัง 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 ลำดับคือ:

  1. ประตูครึ่งหน้าส่วนหน้า (auth, การตรวจสอบ, idempotency shard key) — ไม่เปลี่ยนแปลง
  2. สร้าง ทำเครื่องหมาย และลงชื่อในธุรกรรม Arweave บุคคลที่แน่นอนนี้ นี่มาจาก tx_id ในระดับท้องถิ่น แม้ว่าการสร้างธุรกรรมอาจดึงข้อมูลรางวัล/ข้อมูลเมตาแองเคอร์จากเกตเวย์ ความล้มเหลวในการเตรียมก็ยังทำให้คำขอล้มเหลวก่อนการยืนยัน
  3. แบบซิงโครนัส เขียนชิ้นงานที่เลือกที่ qub-cache/<tx_id> และคงไว้ซึ่งบันทึกการสร้าง-การดำเนินการ/เอาต์บ็อกซ์ที่เสถียร เหล่านี้คือความทนทานและระดับการลองใหม่; ความล้มเหลวก่อนการตัดยอดจะส่งกลับ 503
  4. เมื่อ LOG_DO ถูกกำหนดค่าแล้ว, พยายามพร้อมกัน LogDO.append(leaf). นักเขียนคนเดียวมอบหมาย seq, ขยายโซ่รายการ และอัปเดตแนวหน้า RPC การต่อท้ายทำเพียงเท่านั้น; การปิดแบบเป็นชุดทำงานนอกเส้นทางบนสัญญาณเตือน ขณะนี้ความล้มเหลวของการขนส่ง/แอปพลิเคชันการต่อท้าย ล้มเหลวแบบอ่อนไหว: การตอบสนองยังสามารถประสบความสำเร็จได้แม้ปราศจาก log_seq, receipt, หรือ anchor_status. แม้จะมีความเห็นเกี่ยวกับการนำไปใช้ แต่วันนี้ยังไม่มีการเชื่อมต่อการปรับสมดุลบันทึกอัตโนมัติในภายหลัง
  5. ส่งการยืนยันคืน รวมถึง { log_seq, anchor_status: "pending", receipt } เฉพาะเมื่อ append ส่งค่ากลับ tuple ที่สำเร็จสมบูรณ์เท่านั้น receipt.sig_b64url ว่างเปล่าเมื่อผู้ลงนามใบเสร็จไม่พร้อมใช้งาน; ลูกค้าไม่ควรเรียกค่านั้นว่าถูกลงนามหรือไม่สามารถปฏิเสธได้ การไม่มีทูเพิลหมายถึงการเผยแพร่อย่างถาวรเท่านั้น ไม่ใช่การยอมรับบันทึกความโปร่งใส
  6. ใช้งานเลื่อนเดียวในการโพสต์ธุรกรรมที่ลงนามอย่างถูกต้อง ความสำเร็จจะลบกล่องส่ง; ความล้มเหลวจะทิ้งไว้สำหรับ 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 ข้างต้น; ข้อจำกัดในการเปิดตัวที่ผูกมัด จะถูกกล่าวซ้ำที่ท้าย การปฏิบัติสามารถดำเนินการภายใต้พวกมันได้

  1. เส้นทางเริ่มต้น (kind=0x02) ใบความซื่อสัตย์ — ได้ข้อสรุปแล้ว. จัดส่งแบบแยกสองใบตามที่ระบุ: kind=0x02 ไม่มุ่งมั่นทั้งสองฝ่าย body_hash ก็ไม่ drand_round. ไม่ *_body_hash สนามบนเส้นทางที่ตาบอดต่อไบต์ (มันจะเป็นสัญญาณ "ยืนยัน" ที่ปลอมแต่ชัดเจนที่สุดสำหรับผู้รวมและเป็นความสะดวกที่มาตรา 11 มีให้จากแพ็กเกจ) ทำ ไม่ ต้องการการประทับตราเซิร์ฟเวอร์สำหรับ qubs ที่รับรองโดยบันทึก (ซึ่งจะบังคับให้ข้อความธรรมดาผ่าน Worker และทำลายคูเมืองการทำลายล้างทางคริปโต) ส่วนวงจรสั้นที่บรรยายตัวเองใด ๆ ควรอยู่ใน .qub ซองเอกสารรวม / พิสูจน์เอกสารเป็นฟิลด์ที่ตรวจสอบซ้ำโดยผู้ตรวจสอบ ไม่เคยเป็นฟิลด์ใบไม้ ขีดจำกัดการเรียกร้องที่ยืนยันโดยเจ้าของ: §16.11
  2. ความรับผิดชอบด้านการพูดกำกวมหรือการละเว้น — การออกแบบแก้ไขแล้ว การจัดเตรียมไม่สมบูรณ์ การออกแบบต้องการให้กุญแจรับประทับตราถูกปักไว้ LogProfile และลงนามข้าม anchor_owner, รวมถึงวิธีการตรวจสอบ, การเดินตามห่วงโซ่ก่อนหน้า, และหัวข้อสองส่วนที่ตีพิมพ์ด้วยตนเอง โปรไฟล์ที่คอมไพล์และฮุคสำหรับการปรับใช้ยังคงเป็นเพียงตัวแทน/ไม่บังคับตามที่ระบุใน §16.6 ดังนั้นข้อเรียกร้องที่แข็งแกร่งกว่า ตรวจพบ + ได้รับใบเสร็จ จะไม่เป็นปัจจุบันจนกว่าประตูเหล่านั้นจะปิด ต้องไม่ทำการตลาดว่าเป็น พยานอิสระ จริง พยานบุคคลที่สามจริงจะถูกเลื่อนออกไปในการปรับปรุงการกำกับดูแล §15
  3. รากความน่าเชื่อถือของเจ้าของสมอที่ตรึงไว้ + การหมุน — แก้ไขแล้ว. รับเลี้ยง LogProfile พิน (§16.6); ผู้ตรวจสอบจะตรวจสอบ anchor_tx.owner == anchor_owner และยืนยันข้อมูล tx → การผูก tx_id แบบท้องถิ่น การบริหารการหมุนเวียนคือ §15 ส่วนขยายสำหรับสร้าง (§15.3 การกระตุ้นถูกเพิ่ม), ไม่ใช่การนำกลับมาใช้ใหม่; การหมุนเวียนที่วางแผนไว้ข้ามสัญลักษณ์ การหมุนเวียนที่ขับเคลื่อนด้วยการประนีประนอมจะกลับไปยังการชน §15 พร้อมกับการตรวจสอบส้อมเพื่อจำกัดความเสียหาย.
  4. การทำให้ใบ Private-qub ตาบอด — ได้รับการแก้ไขแล้ว เก็บการทำให้ตาพร่าไว้สำหรับคิวบ์ส่วนตัว (ref = SHA3-256(qub_id ‖ log_blind_secret)), ดิบ qub_id สำหรับ qubs สาธารณะ (มีอยู่แล้ว §16.2.1), chash ในฐานะสายสัมพันธ์อิสระ log_blind_secret เป็นความลับระดับความสัมพันธ์/ซิบอล หมุนไปข้างหน้าเท่านั้น (§16.2.1)
  5. received_at — ได้ข้อสรุปแล้ว. เก็บมันไว้ในใบไม้ มุ่งมั่นแต่ชัดเจนว่าไม่ใช่หลักฐาน; ไม่เคยปรากฏเป็นหลักฐานหรือการยืนยันข้อพิพาทในทุกพื้นผิว การตรวจสอบความสมเหตุสมผลของมอนิเตอร์ใด ๆ จะเปรียบเทียบกับเวลาบล็อกของ Arweave T, ไม่ใช่ที่ควบคุมโดยผู้ปฏิบัติการ anchored_at (§16.6)
  6. การพิสูจน์เวลาแบบเป็นชั้น — การแก้ไขการออกแบบ ไม่ใช่การวางเส้นทางปัจจุบัน การออกแบบที่ได้รับการตรวจสอบมอบเวลาบล็อกยึดให้กับชั้นที่จัดกลุ่ม และการพิสูจน์ชั่วโมงที่แน่นอนให้กับ T3 ที่ชำระเงิน โดยไม่มี SLA ตัวเลขสำหรับอย่างแรก เส้นทางปัจจุบันยังไม่ได้กำหนดความแตกต่างทางการค้านั้น: พวกเขานัดหมายธุรกรรมแต่ละรายการสำหรับการตีพิมพ์ที่ยอมรับ และการบันทึกความครอบคลุมยังคงมีเงื่อนไขดังที่ระบุใน §16.1/§16.10 ข้อความผลิตภัณฑ์ต้องอธิบายการใช้งาน ไม่ใช่การแบ่งชั้นในอนาคตนี้
  7. ต้นสะสมเกี่ยวกับคนงาน — แก้ไขเรียบร้อยแล้ว ต้นไม้ RFC 9162 รวมเดี่ยว + LogDO ผู้เขียนเดี่ยวที่เก็บหน้าแรกแบบแคช (มีพื้นที่ว่างสบายเมื่อเทียบกับเพดาน DO ประมาณ 1k การเขียนต่อวินาที; เลื่อนการแบ่ง Merkle-of-shard-roots จนใกล้ถึงเพดานนั้น) ที่มีคีย์การประสานงาน (level, index) โหนดเก็บ R2 + ตัวเวกเตอร์ทดสอบใบเย็น wiped-DO ถูกนำไปใช้งาน (§16.9) < 300 ms ยังคงเป็นเป้าหมายด้านการออกแบบ/การปฏิบัติการ ไม่ใช่คำสัญญาของโปรโตคอล (§16.10)
  8. โครงการลายเซ็น ANS-104 + แฮชเชิงลึก — แก้ไขแล้ว. RSA-PSS (ประเภทลายเซ็น 1 ใช้ JWK ของกระเป๋าแองเคอร์เฉพาะซ้ำได้); Ed25519 เลื่อนเป็นเส้นทาง PQ §15 แฮชเชิงลึก SHA-384 ที่ทำด้วยมือถูกจำกัดบนฟิกเจอร์ cross-impl ทิศทางทั้งสอง, การตรวจสอบการทำงานร่วมของ reference-bundler แบบสแตติกเท่านั้น, การแชร์-crypto.subtle ไป-กลับ และตัวตรวจสอบการยอมรับ Arweave หลังจากการรวมแพ็กเกจ (§16.8)

ข้อจำกัดในการเปิดตัวแบบผูกมัด (นำไปสู่การดำเนินการ + การตรวจสอบผลิตภัณฑ์/กฎหมาย):


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, สถานะ) — รายงานหลักฐานเจ้าของที่ถูกเติมเต็มแต่เป็นเพียงตัวแทนเป็น เฉพาะการรวม แทนที่จะเป็น ยืนยันการยึดครบถ้วน