Rand Protocol
الوثائق
برامج توضيحية

قرار ائتماني خاص

برنامج سري يشغّل نموذج تقييم منشورًا على بيانات المتقدّم الخاصة، ولا ينشر سوى القبول أو الرفض، مع التزام SHA-256 يربط البيانات بجهة مُصدِّقة - الواجهة، والحساب بالفاصلة الثابتة، وبرنامج Rust الضيف، وسكربت المضيف، والبرنامج الحي على chain 18.

ينشر المُقرِض نموذج التقييم الخاص به. يشغّله المتقدّم على بياناته الخاصة، ثم يسلّم المُقرِض إيصالًا يقول «موافَق عليه» أو «مرفوض». لا يرى المُقرِض البيانات، ولا يستطيع المتقدّم تغيير النموذج أو تغذيته ببيانات مختلَقة.

هذه هي أصغر حالة مفيدة لإثبات حوسبة ببرنامج عام ومعاملات عامة على مدخلات خاصة. النموذج شبكة عصبية من طبقتين بحساب الأعداد الصحيحة. وينطبق الشكل نفسه على أي قاعدة يريد جهة رقابية أو طرف مقابل أن يتمكن من قراءتها: صيغة تسعير، أو اختبار أهلية، أو حدّ مخاطر.

تتضمن صفحة Example programs الإعداد الذي تشترك فيه صفحات الأمثلة الأربع.

1. ما الذي يُثبته

ثلاثة أطراف:

  • المُقرِض ينشر البرنامج ونموذجه في مقطعه العام. يسمّي معرّف البرنامج ذلك النموذج. ويستطيع أي شخص قراءة الأوزان من السلسلة.
  • المُصدِّق (مكتب ائتمان، أو مصرف، أو جهة عمل) يعرف بيانات المتقدّم. يعطي المتقدّم قيم الخصائص، وملحًا عشوائيًا (salt) من 16 بايت، وتوقيعه على الالتزام SHA-256(salt ‖ features).
  • المتقدّم يستدعي البرنامج بالملح والخصائص بوصفها مدخلًا خاصًا.

يُثبت الاستدعاء ما يلي:

  1. كل خاصية تقع ضمن النطاق الذي يعلنه النموذج لها؛
  2. درجة النموذج على تلك الخصائص تساوي العتبة أو تزيد عليها (الحالة 1) أو تقل عنها (الحالة 2)؛
  3. الالتزام المنشور هو SHA-256 للملح ولتلك الخصائص نفسها.

يتحقق المُقرِض من معرّف برنامج الإيصال وحالته ومن أن الالتزام هو ما وقّعه المُصدِّق. ولا يعرف عن المتقدّم سوى بت واحد.

المدخلات الخاصة لا تثبت شيئًا عن العالم بمفردها. فبدون النقطة 3 يستطيع المتقدّم إدخال أي أرقام. الالتزام هو ما يربط الأرقام الواردة في الإثبات بأرقام شهد بها طرف ما. أما التوقيع نفسه فيتحقق منه المُقرِض خارج السلسلة: فالتحقق من Dilithium أو ECDSA لا يتسع له حدّ تكلفة الاستدعاء، ولا حاجة إلى أن يكون داخل الإثبات لأن الالتزام عام.

2. الواجهة

كل كلمة عامة هي i32، مخزَّنة على هيئة u32 بمتمم الاثنين.

الكلمة العامةالمعنى
0عدد الخصائص n، من 1 إلى 16
1عدد الوحدات المخفية h، من 1 إلى 16
2العتبة
3عدد الخانات الكسرية، أي الإزاحة s، من 0 إلى 31
ثم 2n كلمةmin وmax المسموح بهما لكل خاصية
ثم h × n كلمةأوزان الطبقة الأولى، صفًا بصف
ثم h كلمةانحيازات الطبقة الأولى
ثم h كلمةأوزان الطبقة الثانية
ثم كلمة واحدةانحياز الطبقة الثانية
الكلمة الخاصةالمعنى
0 إلى 3الملح، 16 بايت على هيئة أربع كلمات big-endian
4 فصاعدًاالخصائص الـn، كل منها i32
خانة الخرجالمعنى
0الحالة: 1 موافَق عليه، 2 مرفوض، 3 خاصية خارج النطاق، 4 نموذج غير صالح (حجم خارج الحدود، أو فاض الحساب)
1غير مستخدمة
2 إلى 6أول 20 بايت من الالتزام، على هيئة خمس كلمات big-endian
7غير مستخدمة؛ ويكتب فيها هلع (panic) في Rust القيمة 0xdeadbeef

لا يُنشر الالتزام إلا مع الحالة 1 أو 2. أما المدخل الخارج عن النطاق فيتوقف التنفيذ عنده قبل أن يُحسب أي تجزئة.

3. الحساب

النموذج أعداد صحيحة فقط. فالآلة لا تملك فاصلة عائمة، ويجب أن يستطيع المدقِّق (verifier) إعادة إنتاج الدرجة بدقة، وهو ما لا تضمنه الفاصلة العائمة عبر الآلات المختلفة.

hidden[j] = max( (b1[j] + Σ w1[j][i] · x[i]) >> s , 0 )      for each hidden unit j
score     = (b2 + Σ w2[j] · hidden[j]) >> s
approved  = score >= threshold

الأوزان بنقطة ثابتة ذات s خانة كسرية: فمع s = 8 يعني الوزن 256 القيمة 1.0. المجاميع من النوع i64، وكل عملية ضرب وجمع مفحوصة، فالنموذج الذي أوزانه كبيرة بما يكفي للفيضان يعطي الحالة 4 بدل درجة خاطئة. والعملية >> على i64 سالب تقرِّب نحو سالب ما لا نهاية في Rust، وتفعل >> في Python الشيء نفسه، وهذا ما يجعل دالة Python من عشرة أسطر مرجعًا صالحًا.

يضم النموذج التجريبي ثماني خصائص وأربع وحدات مخفية. كُتبت أوزانه يدويًا لهذه الصفحة. إنه ليس نموذجًا مدرَّبًا ولا يدل على شيء في الائتمان.

{
  "shift": 8,
  "threshold": 600,
  "features": [
    {"name": "age_years", "min": 18, "max": 120},
    {"name": "income_thousands", "min": 0, "max": 100000},
    {"name": "debt_to_income_pct", "min": 0, "max": 1000},
    {"name": "on_time_payments_pct", "min": 0, "max": 100},
    {"name": "utilisation_pct", "min": 0, "max": 100},
    {"name": "history_years", "min": 0, "max": 100},
    {"name": "inquiries_12m", "min": 0, "max": 100},
    {"name": "delinquencies", "min": 0, "max": 100}
  ],
  "w1": [
    [  64,   96,  -512,  1536,  -640,  1024, -2048, -8192],
    [   0,  256, -1024,     0,     0,     0,     0,     0],
    [   0,    0,     0,  2560, -1280,     0, -4096, -16384],
    [ 128,    0,     0,     0,     0,  2048,     0,     0]
  ],
  "b1": [25600, 12800, -128000, 0],
  "w2": [160, 96, 224, 128],
  "b2": 76800
}

4. البرنامج الضيف

my-guests/score/src/main.rs، كاملًا:

#![no_std]
#![no_main]

use guest_sdk::{halt, read_input, read_public, sha256_compress, write_output};

const MAX_FEATURES: usize = 16;
const MAX_HIDDEN: usize = 16;

const APPROVED: u32 = 1;
const DECLINED: u32 = 2;
const OUT_OF_RANGE: u32 = 3;
const BAD_MODEL: u32 = 4;

/// SHA-256's initial state (FIPS 180-4 §5.3.3).
const IV: [u32; 8] = [
    0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
];

/// SHA-256 of `words`, each word taken as four big-endian bytes. The message is whole words,
/// so the padding is whole words too: `0x80000000`, zeros, then the bit length in the last
/// word of a block (the length's high word is always zero here).
fn sha256_words(words: &[u32]) -> [u32; 8] {
    let mut buf = [0u32; 24];
    buf[16..].copy_from_slice(&IV);
    let mut fill = 0;
    for &w in words {
        buf[fill] = w;
        fill += 1;
        if fill == 16 {
            sha256_compress(&mut buf);
            fill = 0;
        }
    }
    buf[fill] = 0x8000_0000;
    fill += 1;
    if fill > 14 {
        buf[fill..16].fill(0);
        sha256_compress(&mut buf);
        fill = 0;
    }
    buf[fill..15].fill(0);
    buf[15] = 32 * words.len() as u32;
    sha256_compress(&mut buf);
    let mut out = [0u32; 8];
    out.copy_from_slice(&buf[16..]);
    out
}

/// `weight · x`, added to `acc`; `None` when it does not fit an i64.
fn mul_add(acc: i64, weight: u32, x: i64) -> Option<i64> {
    acc.checked_add((weight as i32 as i64).checked_mul(x)?)
}

/// Runs the model in the public segment over the private features.
fn score(features: &[i32], hidden: usize, shift: u32) -> Option<i64> {
    let n = features.len();
    // The public segment after the header and the feature ranges:
    // hidden × n first-layer weights, hidden biases, hidden second-layer weights, one bias.
    let w1 = 4 + 2 * n;
    let b1 = w1 + hidden * n;
    let w2 = b1 + hidden;
    let b2 = w2 + hidden;

    let mut out = read_public(b2 as u32) as i32 as i64;
    for j in 0..hidden {
        let mut acc = read_public((b1 + j) as u32) as i32 as i64;
        for (i, &x) in features.iter().enumerate() {
            acc = mul_add(acc, read_public((w1 + j * n + i) as u32), x as i64)?;
        }
        // Fixed point: drop the fractional bits, then ReLU.
        let h = (acc >> shift).max(0);
        out = mul_add(out, read_public((w2 + j) as u32), h)?;
    }
    Some(out >> shift)
}

/// Public segment, fixed at deploy (every value an i32):
///   [features n, hidden units, threshold, fractional bits,
///    n × (min, max), hidden × n weights, hidden biases, hidden weights, bias]
/// Private input: [salt (4 words), n features].
/// Outputs: [status, 0, commitment (first 5 words), 0], where
/// commitment = SHA-256(salt ‖ features), every word big-endian.
#[no_mangle]
pub extern "C" fn main() -> ! {
    let n = read_public(0) as usize;
    let hidden = read_public(1) as usize;
    let threshold = read_public(2) as i32 as i64;
    let shift = read_public(3);
    if n == 0 || n > MAX_FEATURES || hidden == 0 || hidden > MAX_HIDDEN || shift > 31 {
        write_output(0, BAD_MODEL);
        halt();
    }

    // The committed message: the salt, then the features, in input order.
    let mut message = [0u32; 4 + MAX_FEATURES];
    for (i, w) in message.iter_mut().enumerate().take(4 + n) {
        *w = read_input(i as u32);
    }
    let mut features = [0i32; MAX_FEATURES];
    let mut in_range = true;
    for i in 0..n {
        let x = message[4 + i] as i32;
        let min = read_public(4 + 2 * i as u32) as i32;
        let max = read_public(5 + 2 * i as u32) as i32;
        in_range &= min <= x && x <= max;
        features[i] = x;
    }
    if !in_range {
        write_output(0, OUT_OF_RANGE);
        halt();
    }

    let status = match score(&features[..n], hidden, shift) {
        Some(s) if s >= threshold => APPROVED,
        Some(_) => DECLINED,
        None => BAD_MODEL,
    };
    let commitment = sha256_words(&message[..4 + n]);

    write_output(0, status);
    for k in 0..5 {
        write_output(2 + k as u32, commitment[k]);
    }
    halt();
}

النموذج بيانات لا شيفرة. تُقرأ الأوزان بـread_public، فتخدم صورة منشورة واحدة أي نموذج يصل إلى 16 خاصية و16 وحدة مخفية. وتغيير النموذج هو نشر جديد للشيفرة نفسها بمقطع عام جديد، وبمعرّف برنامج جديد. لا يستطيع المُقرِض تبديل الأوزان من تحت المتقدّم، ولا يستطيع المتقدّم اختيار نموذج أكثر تساهلًا: فالإيصال يسمّي معرّف البرنامج، والمعرّف يربط الأوزان.

النطاقات جزء من النموذج. فبدونها لا يستطيع المتقدّم الكذب في قيمة (الالتزام يمنع ذلك)، لكن يستطيع المُصدِّق توقيع قيمة بلا معنى، مثل عمر −5، قد يكافئها نموذج ذو وزن سالب. كما أن الفحص يحدّ الحساب.

للملح وظيفتان. فهو يجعل الالتزام عصيًّا على التخمين: ثمانية أعداد صحيحة صغيرة قليلة الإنتروبيا، وبلا ملح يستطيع أي شخص تجزئة متقدّمين محتملين ومقارنتهم. وهو يمرّر 128 بتًا عشوائيًا عبر الحوسبة. فقبل أن تُعمّي مجموعة القيود 7 مجاميع الناقل (bus) في الإثبات، كان يمكن كسر استدعاء يضم بضع كلمات خاصة صغيرة بالتخمين الشامل انطلاقًا من الإثبات نفسه، وكانت ملاحظات العقدة الكاملة (fullnode) تطلب من هذه البرامج قراءة كلمات عشوائية. تشغّل السلسلة 18 مجموعة القيود 8 التي أُصلح فيها ذلك، لكن الملح يكلّف أربع كلمات إدخال ويُنهي السؤال.

5. جانب المضيف

يستخدم score.py المكتبة القياسية في Python وحدها:

score.py setup   <model.json>                            writes public.txt
score.py witness <model.json> <salt: 32 hex> <x0> <x1> …  prints the private words;
                                                        stderr: the score, the decision, the commitment
score.py commit  <salt: 32 hex> <x0> <x1> …              prints the commitment alone

المُقيِّم المرجعي والالتزام، كاملين:

def score(m, x):
    """The guest's arithmetic exactly: integers, a floor shift after each layer, ReLU between."""
    out = m["b2"]
    for row, b, w2 in zip(m["w1"], m["b1"], m["w2"]):
        h = max((b + sum(w * xi for w, xi in zip(row, x))) >> m["shift"], 0)
        out += w2 * h
    return out >> m["shift"]

def commitment(salt: bytes, x):
    return hashlib.sha256(salt + b"".join(struct.pack(">i", xi) for xi in x)).digest()
$ cd my-guests/score
$ python3 score.py setup model.json
wrote public.txt (61 words)
$ cat public.txt
8 4 600 8 18 120 0 100000 0 1000 0 100 0 100 0 100 0 100 0 100 64 96 4294966784 1536 4294966656 1024 4294965248 4294959104 0 256 4294966272 0 0 0 0 0 0 0 0 2560 4294966016 0 4294963200 4294950912 128 0 0 0 0 2048 0 0 25600 12800 4294839296 0 160 96 224 128 76800

الأعداد الكبيرة هي الأوزان السالبة: 4294966784 هو −512.

اختُبر البرنامج الضيف مقابل هذا المرجع على 300 متقدّم عشوائي، بمقارنة القرار وكلمات الالتزام الخمس كلها، وعلى متقدّمين درجاتهم 599 و600. وتطابقت النتائج في كل حالة.

6. البناء والتشغيل

$ rand-guest build my-guests/score --max-words 65535
462 words against a cap of 65535 (fits); 0 ecall(s) with a non-static a7
OK
wrote my-guests/score/image.bin and its .sha256 (462 words, hc c63d7b15a165a11d007ce48bc37563b71f77fcc4991ffe0182c8cf4c77b6e7b5, program id ce8d234faf737c24bdd2826933ac843973c140c53e76fbb2cccaef3073da9818)

متقدّم عمره 41 ودخله 85 ألفًا، ونسبة دينه إلى دخله 28%، و97% من مدفوعاته في موعدها، واستخدامه للائتمان 22%، وتاريخه 12 عامًا، واستعلام واحد وبلا تعثّر:

$ python3 score.py witness model.json 00112233445566778899aabbccddeeff 41 85 28 97 22 12 1 0 > witness.txt
score 1075 approved
commitment 9430c3818f5966f8dd33f78e05a7d079355191e6
$ cat witness.txt
1122867 1146447479 2291772091 3437096703 41 85 28 97 22 12 1 0
$ rand-guest run image.bin --public $(cat public.txt) --input $(cat witness.txt) --tier 14
out[0] = 1
out[1] = 0
out[2] = 2486223745
out[3] = 2405000952
out[4] = 3711170446
out[5] = 94883961
out[6] = 894538214
out[7] = 0
cycles 1810
tier 12
tier 14: fits (cycles 1946 of 16383, Poseidon2 permutations 136 of 2048)

العدد 2486223745 هو 0x9430c381، أي أول أربعة بايتات من الالتزام. أما الدرجة 1075 فلا تظهر في أي موضع من الخرج.

متقدّم ثانٍ (23 و31 و55 و82 و91 و2 و6 و2، بملح آخر) درجته 417 فيعطي out[0] = 2 مع التزامه الخاص، في 1,810 دورة نفسها. والمتقدّم الأول بعد تغيير العمر إلى 17 يعطي out[0] = 3 بلا التزام.

7. الميزانية

البرنامج462 كلمة
المقطع العام61 كلمة
الكلمات الخاصة12
الدورات1,810
عمليات الضغط في SHA-2561 (48 بايتًا من الرسالة تتسع في كتلة واحدة)
الفئة12

هذا أرخص البرامج الأربعة. المرور الأمامي 36 عملية ضرب. أما نموذج عند حدود البرنامج، بـ16 خاصية و16 وحدة مخفية، فهو 272 عملية ضرب ومقطع عام من 325 كلمة. ويقيس 7,900 دورة في المحاكي، أي نحو نصف الفئة 14.

لا يعتمد التشغيل على البيانات: 200 متقدّم عشوائي، من المقبولين والمرفوضين، استغرق كل منهم 1,810 دورة بالضبط. وحدّ الغاز الذي يعلنه الاستدعاء عام ويُقرَّب من غاز التشغيل، فالبرنامج الذي يتبع عدد دوراته مدخلاته يسرّب منها شيئًا يسيرًا. وهذا البرنامج لا شيء لديه يُسرَّب بهذه الطريقة.

منذ السلسلة 18 تطبع المحفظة الغاز الدقيق للتشغيل والحد الذي تعلنه:

gas: 2011 (declaring 2048, tier 12)

1,948 صفًا بغاز واحد لكل منها، إضافةً إلى 63 للضغط الواحد.

8. النشر

تُدفع تكلفة كلمات النموذج الـ61 كما تُدفع كلمات الشيفرة: 0.001 + 0.0001 × (462 + 61) = 0.0533 RAND.

$ rand fee deploy 462 --public-words 61
0.0533 RAND
$ rand program deploy image.bin --public public.txt
program id: 7bbec14289f4446231b3a9a5ac18cb0613a373ca8a14fe4f046f3fde7ae6c73c (462 words, hc 157b3dc61da165a18be47c00b76375c3c4fc771f01fe1f994ccfc882b5e7b677, public input 61 words, digest a08a7c691c45babeb39e953930d4d7174662b7b8adcc2d0f4e6c193d362d6668)
proving the spend authorisation on this machine (tier 10; seconds)…
authorisation proved in 7.4s: tier 10, 1364940 bytes
proving the bundle (tier 14; about a minute and a half on a laptop)…
proved in 123.5s: tier 14, 1497319 bytes
submitted deploy d47a9323ea53bfafc3faa36d004416d374bf3c2a48971d87e346be426b0caf14
  0 RAND out, 59.7229 RAND change, fee 0.0533 RAND, anchored at height 98062, proved in 123.5s (bundle) + 7.4s (auth)

9. الاستدعاء

$ rand call 7bbec14289f4446231b3a9a5ac18cb0613a373ca8a14fe4f046f3fde7ae6c73c \
    $(sed 's/[0-9][0-9]*/--input &/g' witness.txt)
proving the call locally (12 inputs stay private, over the program's 61-word public input)…
gas: 2011 (declaring 2048, tier 12)
fee 0.005673037 RAND (incl. two price steps of headroom)
proved in 30.6s: tier 12, 1838256 bytes, outputs [1, 0, 2486223745, 2405000952, 3711170446, 94883961, 894538214, 0]
proving the spend authorisation on this machine (tier 10; seconds)…
authorisation proved in 6.8s: tier 10, 1369611 bytes
proving the bundle (tier 14; about a minute and a half on a laptop)…
proved in 124.3s: tier 14, 1499112 bytes
submitted call 454c1b9eb1e159e682d4ff26b71f77d8a950b813533d91a89293dd432fdcf6f7
  0 RAND out, 59.696058627 RAND change, fee 0.005673037 RAND, anchored at height 98832, proved in 124.3s (bundle) + 6.8s (auth)
{
  "h_in": "ef80c744a8cdea62b56e334baaf7372e4a2e6c8282d37aa8ae96b4f039ec1a22",
  "h_pub": "a08a7c691c45babeb39e953930d4d7174662b7b8adcc2d0f4e6c193d362d6668",
  "height": 98993,
  "index": 0,
  "outputs": [
    1,
    0,
    2486223745,
    2405000952,
    3711170446,
    94883961,
    894538214,
    0
  ],
  "program": "7bbec14289f4446231b3a9a5ac18cb0613a373ca8a14fe4f046f3fde7ae6c73c",
  "tier": 12,
  "tx": "454c1b9eb1e159e682d4ff26b71f77d8a950b813533d91a89293dd432fdcf6f7"
}
input transcript published; open it with `rand open-call 454c1b9eb1e159e682d4ff26b71f77d8a950b813533d91a89293dd432fdcf6f7`

الحالة 1 والالتزام 9430c381 8f5966f8 dd33f78e 05a7d079 355191e6 بالست عشرية. وh_pub هو ملخّص كلمات النموذج الـ61. أما الخصائص والملح والدرجة فليست في المعاملة.

استغرق الاستدعاء أكثر من ثلاث دقائق بقليل على حاسوب محمول بمعالج Apple Silicon: 31 ثانية لإثبات الاستدعاء، و7 ثوانٍ لتفويض الإنفاق، و124 ثانية للحزمة، بذروة 6.2 GB. وحجم الإثبات 1,838,256 بايتًا.

10. تحقّق بنفسك

جانب المُقرِض، بالإيصال والالتزام الموقَّع من المُصدِّق فقط:

  1. النموذج. تعيد rand_getProgramPublic على معرّف البرنامج الكلمات الـ61. قارنها بملف model.json المنشور (يعيد score.py setup بناءها).
  2. الشيفرة. أعد بناء my-guests/score عند الإيداع نفسه للدوائر وقارن hc بـcode_hash للبرنامج.
  3. الإيصال. program هو معرّف النموذج، والخرج 0 يساوي 1، والخانة 7 ليست 3735928559.
  4. الالتزام. الخرجان 2 إلى 6، مطبوعةً بالست عشرية، هي أول 20 بايتًا من الالتزام الذي وقّعه المُصدِّق:
$ python3 score.py commit 00112233445566778899aabbccddeeff 41 85 28 97 22 12 1 0
9430c3818f5966f8dd33f78e05a7d079355191e6

الرفض إيصال أيضًا. استدعاء المتقدّم الثاني، 119e7834…1b3b على السلسلة 18 (وتكرّر على السلسلة 20 في c8a60183…9f73، وهو يرفض كذلك)، جرى إثباته وتثبيته بالطريقة نفسها:

$ python3 score.py witness model.json 0f1e2d3c4b5a69788796a5b4c3d2e1f0 23 31 55 82 91 2 6 2 > witness.txt
score 417 declined
commitment 0313ea462178574c49a10fe7ea1d84207652e1a3
$ rand call 7bbec14289f4446231b3a9a5ac18cb0613a373ca8a14fe4f046f3fde7ae6c73c \
    $(sed 's/[0-9][0-9]*/--input &/g' witness.txt)
…
gas: 2011 (declaring 2048, tier 12)
proved in 26.1s: tier 12, 1836624 bytes, outputs [2, 0, 51636806, 561534796, 1235292135, 3927802912, 1985143203, 0]
…
submitted call 119e7834033edd19bba8e10c1a764e8bdbc0b1c8415a42f041faa392c6411b3b

الحالة 2، والعدد 51636806 هو 0x0313ea46. الغاز والحد المعلن والمستوى هي نفسها في الاستدعاء المقبول، فيختلف الاستدعاءان على السلسلة في كلمة الحالة والالتزام ولا يختلفان في شيء آخر. ليس لدى المتقدّم سبب لنشر الرفض، لكن للمُقرِض أن يطلب الإيصال في الحالتين، ولا يستطيع المتقدّم المرفوض أن يقدّم إيصال قبول.

ما لا يوفّره الإثبات

  • صحة البيانات. يُظهر الإثبات أن النموذج عمل على القيم الملتزَم بها. أما صحة هذه القيم فمرجعها كلمة الجهة المُصدِّقة، وعلى المُقرِض أن يثق بتوقيعها في ذلك.
  • متقدّم لا يمكن ربطه. الالتزام معرّف ثابت: تستطيع الجهة المُصدِّقة التعرّف عليه، وإيصالان لهما الالتزام نفسه يعودان إلى المتقدّم نفسه والبيانات نفسها. ملح جديد من الجهة المُصدِّقة لكل طلب يعالج الأمر الثاني، ولا يعالج الأول.
  • نموذج سرّي. الأوزان علنية. وهذا هو المقصود في قاعدة يجب أن يتمكّن الناس من قراءتها، وهو عيب لمُقرِض يعدّ نموذجه سرًّا تجاريًا. ويستطيع المتقدّم كذلك أن يبحث عن المدخلات التي ستنجح، لكنه لا يستطيع استخدامها دون توقيع من الجهة المُصدِّقة عليها.
  • حماية من الاستكشاف على يد جهة مُصدِّقة غير أمينة. من يعرف الخصائص يستطيع أن يشغّل النموذج العلني بنفسه. والخصوصية هنا هي خصوصية المتقدّم تجاه المُقرِض والجمهور، لا تجاه الطرف الذي قدّم البيانات.
  • نموذج كبير. ست عشرة خاصية وست عشرة وحدة مخفية بطاقة تقييم، لا شبكة عميقة. الاستدعاء محدود بالمستوى 14، أي نحو ستة عشر ألف دورة.

آخر تحقق مقابل الإيداع 48dc04b في fullnode (السلسلة 18) بتاريخ 2026-09-30، وcircuits `aeacf31` (`guest-sdk/src/lib.rs`, `rand-guest`), the commit fullnode v0.6.7 vendors; every `rand-guest`, `python3` and `rand` output on this page was produced on 2026-09-30, the deploy and the call on chain 18. إذا اختلف الكود عن هذه الصفحة فالكود هو الصحيح؛ أبلغونا بذلك.