Rand Protocol
ஆவணமாக்கல்
எடுத்துக்காட்டு நிரல்கள்

ஒரு அநாமதேய வாக்கெடுப்பு

எந்த உறுப்பினர் என்பதை வெளிப்படுத்தாமல் ஒரு Keccak மெர்க்கல் அனுமதிப் பட்டியலில் உறுப்பினர் என்பதை நிரூபித்து, ஒரு தேர்வையும் இரண்டாவது வாக்கைத் தடுக்கும் ஒரு nullifier-ஐயும் வெளியிடும் ஒரு ரகசிய நிரல் - இடைமுகம், Rust guest, host ஸ்கிரிப்ட், வரவுசெலவு வரம்பு, மற்றும் chain 18-இல் இயங்கும் நிரல்.

இந்த நிரல், ஒரு நிலையான பட்டியலில் உள்ள உறுப்பினர் ஒருவர், தாம் எந்த உறுப்பினர் என்பதைக் காட்டாமல் ஒரு வாக்கை அளிக்க அனுமதிக்கிறது. அந்தப் பட்டியல் Keccak-256 ஹாஷ்களால் ஆன ஒரு Merkle மரம்; Ethereum airdrop பட்டியல் கட்டப்படும் முறையிலேயே இது கட்டப்படுகிறது, எனவே அதே கருவிகளே இதையும் உருவாக்குகின்றன. இந்த நிரல் சங்கிலி 18-இல் நிறுவப்பட்டுள்ளது; கடைசிப் பிரிவு, நீங்களே பயன்படுத்தக்கூடிய மாதிரி உறுப்பினர்களைக் கொண்டு அதை அழைக்கிறது.

நான்கு எடுத்துக்காட்டுப் பக்கங்களுக்கும் பொதுவான அமைப்பு எடுத்துக்காட்டு நிரல்கள் பக்கத்தில் உள்ளது: circuits checkout, வாலெட், மூலக் குறியீட்டை எங்கே பெறுவது என்பவை.

1. இது எதை நிரூபிக்கிறது

ஒரு ஒருங்கிணைப்பாளரிடம் உறுப்பினர்களின் பட்டியல் உள்ளது. ஒவ்வொரு உறுப்பினரும் 32-பைட் ரகசியம் ஒன்றை வைத்திருக்கிறார். ஒருங்கிணைப்பாளர் அந்த ரகசியங்களை ஒருபோதும் காண்பதில்லை: ஒவ்வொரு உறுப்பினரும் keccak256(secret ‖ tag) என்ற ஒரு இலையை ஒப்படைக்கிறார், ஒருங்கிணைப்பாளர் அந்த இலைகளின் மீது ஒரு Merkle மரத்தைக் கட்டுகிறார்.

ஒரு வாக்கு என்பது ஒரு அழைப்பு. வேறு எதையும் வெளிப்படுத்தாமல் அது பின்வருவனவற்றை நிரூபிக்கிறது:

  1. அழைப்பவருக்கு ஒரு ரகசியம் தெரியும், அதன் இலை மரத்தில் உள்ளது;
  2. வெளியிடப்பட்ட தேர்வு, அனுமதிக்கப்பட்ட தேர்வுகளில் ஒன்று;
  3. வெளியிடப்பட்ட நலிஃபையர் keccak256(secret ‖ tag ‖ poll id) ஆகும்.

ஒரு வாக்கெடுப்பில் ஒரு ரகசியம் அளிக்கும் ஒவ்வொரு வாக்குக்கும் நலிஃபையர் ஒன்றாகவே இருக்கும்; வேறு ஒவ்வொரு வாக்கெடுப்பிலும் அது வேறுபடும். வாக்குகளை எண்ணுபவர் ஒவ்வொரு நலிஃபையரையும் ஒருமுறை மட்டுமே எண்ணுகிறார். அது இலையுடன் இணைவதில்லை: ஒரு நலிஃபையரிலிருந்து இலைக்குத் திரும்பச் செல்ல ரகசியம் தேவை.

சங்கிலி இரண்டாவது வாக்கை நிராகரிப்பதில்லை. Rand-இல் உள்ள ஒரு நிரலுக்குச் சேமிப்பகம் இல்லை, எனவே அது ஒரு நலிஃபையரை நினைவில் வைத்திருக்க முடியாது (நிரல்கள்). ஒரே ரகசியத்துடன் செய்யப்படும் இரண்டு அழைப்புகளும் சரிபார்க்கப்பட்டு, இரண்டும் ரசீது ஒன்றை விட்டுச் செல்கின்றன. “ஒரு நலிஃபையர், ஒரு வாக்கு” என்ற விதியை, ரசீதுகளின் மீது எண்ணுபவரே பயன்படுத்துகிறார். அதே எண்ணிக்கையை யார் வேண்டுமானாலும் நடத்தலாம்; அதுவே மொத்த எண்ணிக்கையைச் சரிபார்க்கக்கூடியதாக்குகிறது.

2. இடைமுகம்

மரம், வாக்கெடுப்பு, தேர்வுகளின் எண்ணிக்கை ஆகியவை ஒவ்வொரு வாக்குக்கும் ஒன்றாகவே இருப்பதால், அவை நிறுவும்போதே நிலைநிறுத்தப்படும் பொதுப் பிரிவில் இடம்பெறுகின்றன. அதனால் நிரல் id இந்த வாக்கெடுப்பை மட்டுமே குறிக்கிறது, வேறு எதையும் அல்ல.

பொதுச் சொல்பொருள்
0மர ஆழம் d, 1 முதல் 20 வரை
1வாக்கெடுப்பு id
2தேர்வுகளின் எண்ணிக்கை; ஒரு தேர்வு 0 .. choices − 1
3 முதல் 10வேர், 32 பைட்டுகள், எட்டு லிட்டில்-எண்டியன் சொற்களாக
தனிப்பட்ட சொல்பொருள்
0 முதல் 7ரகசியம்
8உறுப்பினரின் இலைக் குறியெண்
9தேர்வு
10 முதல்Merkle பாதை: இலையிலிருந்து மேல்நோக்கி, ஒவ்வொன்றும் 8 சொற்கள் கொண்ட d உடன்பிறப்புகள்
வெளியீட்டு இடம்பொருள்
0நிலை: 1 வாக்களிக்கப்பட்டது, 2 உறுப்பினர் அல்ல, 3 தேர்வு வரம்புக்கு வெளியே, 4 தவறான அமைப்பு
1தேர்வு
2 முதல் 6நலிஃபையரின் முதல் 20 பைட்டுகள், ஐந்து லிட்டில்-எண்டியன் சொற்களாக
7பயன்படுத்தப்படவில்லை; Rust panic இங்கே 0xdeadbeef எழுதும்

32-பைட் ஹாஷ் என்பது எட்டு சொற்கள்; வெளியீடு முழுவதுமே எட்டு சொற்கள்தான். 160 பிட்களை வெளியிடுவது நிலைக்கும் தேர்வுக்கும் இடம் விடுகிறது. இரண்டு உறுப்பினர்கள் ஒரே 160-பிட் நலிஃபையரைப் பகிர்வது ஹாஷ் மோதலால் மட்டுமே நிகழும்; அதைக் கண்டுபிடிக்க ஏறத்தாழ 2⁸⁰ முயற்சிகள் தேவை.

பைட் வரிசை. Keccak பைட்டுகளின் மீது இயங்குகிறது. இயந்திரத்தின் KECCAK syscall நிலையைச் சொற்களாக எடுத்துக்கொள்கிறது; நிலையின் பைட் i, சொல் i / 4-இன் பைட் i mod 4-இல் இருக்கும். எனவே ஒரே நேரத்தில் நான்கு பைட்டுகளை லிட்டில்-எண்டியனாகப் படிப்பதன் மூலம் 32 பைட்டுகள் எட்டு சொற்களாகின்றன; பொதுப் பிரிவில் உள்ள வேர், அந்த முறையில் படிக்கப்பட்ட வேரின் பைட்டுகளே.

3. விருந்தினர் நிரல்

my-guests/allowlist/src/main.rs, முழுமையாக:

#![no_std]
#![no_main]

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

const MAX_DEPTH: u32 = 20;

const OK: u32 = 1;
const NOT_MEMBER: u32 = 2;
const BAD_CHOICE: u32 = 3;
const BAD_SETUP: u32 = 4;

/// The 32 bytes hashed after a secret to make its leaf: the ASCII tag, zero-padded.
const LEAF_TAG: [u32; 8] = tag_words(b"rand-allowlist-leaf-v1");
/// The first 28 bytes hashed after a secret to make its nullifier; the poll id is the last 4.
const NULL_TAG: [u32; 8] = tag_words(b"rand-allowlist-null-v1");

/// An ASCII tag as eight words: byte `i` of the tag is byte `i % 4` of word `i / 4`.
const fn tag_words(tag: &[u8]) -> [u32; 8] {
    let mut w = [0u32; 8];
    let mut i = 0;
    while i < tag.len() {
        w[i / 4] |= (tag[i] as u32) << (8 * (i % 4));
        i += 1;
    }
    w
}

/// Keccak-256 of a 64-byte message, the hash Ethereum calls `keccak256(abi.encodePacked(a, b))`.
/// The message is one rate block, so this is one permutation: the two halves go into the first
/// 16 words of the state, `0x01` follows them, and `0x80` closes the 136-byte rate.
///
/// `a_second` is 0 or 1. When it is 1 the halves trade places. The place is computed as an
/// offset, not chosen by a branch, so both orders execute the same instructions.
fn hash2(a: &[u32; 8], b: &[u32; 8], a_second: u32) -> [u32; 8] {
    let at = 8 * (a_second & 1) as usize;
    let mut st = [0u32; 50];
    st[at..at + 8].copy_from_slice(a);
    st[8 - at..16 - at].copy_from_slice(b);
    st[16] = 0x01;
    st[33] = 0x8000_0000;
    keccak(&mut st);
    let mut out = [0u32; 8];
    out.copy_from_slice(&st[..8]);
    out
}

fn read8(at: u32) -> [u32; 8] {
    let mut w = [0u32; 8];
    for (i, x) in w.iter_mut().enumerate() {
        *x = read_input(at + i as u32);
    }
    w
}

/// Public segment, fixed at deploy: [depth, poll id, number of choices, root (8 words)].
/// Private input: [secret (8 words), leaf index, choice, siblings (8 words per level)].
/// Outputs: [status, choice, nullifier (5 words), 0].
#[no_mangle]
pub extern "C" fn main() -> ! {
    let depth = read_public(0);
    let poll = read_public(1);
    let choices = read_public(2);
    if depth == 0 || depth > MAX_DEPTH {
        write_output(0, BAD_SETUP);
        halt();
    }

    let secret = read8(0);
    let index = read_input(8);
    let choice = read_input(9);

    // Walk from the leaf to the root. Bit `level` of the index says whether the running hash
    // is the right child at that level.
    let mut cur = hash2(&secret, &LEAF_TAG, 0);
    let mut level = 0;
    while level < depth {
        let sib = read8(10 + 8 * level);
        cur = hash2(&cur, &sib, index >> level);
        level += 1;
    }

    let mut diff = 0u32;
    for k in 0..8 {
        diff |= cur[k] ^ read_public(3 + k as u32);
    }

    let mut ctx = NULL_TAG;
    ctx[7] = poll;
    let nullifier = hash2(&secret, &ctx, 0);

    if diff != 0 {
        write_output(0, NOT_MEMBER);
    } else if choice >= choices {
        write_output(0, BAD_CHOICE);
    } else {
        write_output(0, OK);
        write_output(1, choice);
        for k in 0..5 {
            write_output(2 + k as u32, nullifier[k]);
        }
    }
    halt();
}

இதில் மூன்று விஷயங்கள் பின்பற்றத் தக்கவை.

ஒரு ஹாஷுக்கு ஒரு வரிசைமாற்றம். guest_sdk::keccak256 எந்த பைட் சரத்தையும் ஏற்கிறது, அதற்கான விலையை பைட் சுழல்களால் செலுத்துகிறது. இங்குள்ள ஒவ்வொரு செய்தியும் சரியாக 64 பைட்டுகள், 136-பைட் விகிதத்தைவிடக் குறைவு; எனவே நிரப்புதல் இரண்டு மாறிலிச் சொற்கள், ஹாஷ் என்பது சொல் நகல்களால் நிரப்பப்பட்ட நிலையின் மீது ஒரே ஒரு KECCAK அழைப்பு. Ethereum சொற்களில், இரண்டு bytes32-க்கு hash2(a, b, 0) என்பது keccak256(abi.encodePacked(a, b)) ஆகும்.

இலையையும் நலிஃபையரையும் மரக் கணுவுடன் குழப்ப முடியாது. ஒரு கணு இரண்டு 32-பைட் சேய் ஹாஷ்களை ஹாஷ் செய்கிறது. ஒரு இலை, ஒரு ரகசியத்தையும் நிலையான ASCII குறிச்சொல்லையும் ஹாஷ் செய்கிறது; ஒரு நலிஃபையர், ஒரு ரகசியம், மற்றொரு குறிச்சொல், வாக்கெடுப்பு id ஆகியவற்றை ஹாஷ் செய்கிறது. ஒரு இலை கணுவாகக் கடந்து செல்ல, அதன் சேய்களில் ஒன்று குறிச்சொல்லுக்குச் சமமாக இருக்க வேண்டும்; அதற்கு Keccak முன்னுருவைக் கண்டுபிடிக்க வேண்டும்.

குறியெண்ணின் மீது கிளைபிரியாமல் பாதை கடக்கப்படுகிறது. முதல் பதிப்பு இரண்டு பாதிகளின் வரிசையை மறைமுகமூடி இடமாற்றத்தால் தேர்ந்தெடுத்தது; தொகுப்பி அந்த மறைமுகமூடியை மீண்டும் ஒரு கிளையாக மாற்றியது: இலைக் குறியெண்ணில் உள்ள ஒவ்வொரு 1 பிட்டுக்கும் ஓட்டம் 9 சுழற்சிகள் கூடுதலாக எடுத்தது. சுழற்சி எண்ணிக்கை தானாக வெளியிடப்படுவதில்லை, ஆனால் ஒரு அழைப்பு அறிவிக்கும் கேஸ் வரம்பு அதிலிருந்து முழுமையாக்கப்படுகிறது (பிரிவு 6); எனவே குறியெண்ணைச் சார்ந்த ஓட்டம் குறியெண்ணைச் சிறிதளவு கசியவிடக்கூடும். ஒவ்வொரு பாதியையும் கணக்கிடப்பட்ட இடப்பெயர்வில் எழுதுவது அதைச் சரிசெய்தது: இப்போது ஒவ்வொரு இலைக் குறியெண்ணும் அதே 3,208 சுழற்சிகளில் இயங்குகிறது.

4. ஹோஸ்ட் பக்கம்

இயந்திரம் கணக்கிடும் அதே Keccak ஒருங்கிணைப்பாளருக்கும் வாக்காளர்களுக்கும் தேவை. allowlist.py எந்தச் சார்புகளும் இல்லாத Python ஸ்கிரிப்ட்; அதற்கென சொந்த Keccak-f[1600] உள்ளது (Python-இன் hashlib.sha3_256 என்பது SHA-3; அது வேறுவிதமாக நிரப்பி வேறுபட்ட டைஜஸ்ட்களைத் தருகிறது). அது இரண்டு பணிகளைச் செய்கிறது:

allowlist.py setup  <depth> <poll id> <choices> <secrets file>   writes public.txt, prints the root
allowlist.py witness <depth> <poll id> <secrets file> <member> <choice>
                                                   prints the private words and the nullifier

ஸ்கிரிப்டின் சொற்களில், முழு மரம்:

LEAF_TAG = b"rand-allowlist-leaf-v1".ljust(32, b"\0")
NULL_TAG = b"rand-allowlist-null-v1".ljust(28, b"\0")
EMPTY = bytes(32)  # an unused leaf: no secret hashes to it

def levels(depth, secrets):
    row = [keccak256(s + LEAF_TAG) for s in secrets]
    row += [EMPTY] * ((1 << depth) - len(row))
    out = [row]
    while len(row) > 1:
        row = [keccak256(row[i] + row[i + 1]) for i in range(0, len(row), 2)]
        out.append(row)
    return out

nullifier = keccak256(secret + NULL_TAG + poll.to_bytes(4, "little"))

நடைமுறைப் பயன்பாட்டில் ஒருங்கிணைப்பாளர் இலைகளை வைத்திருக்கிறார், ரகசியங்களை அல்ல; ஒவ்வொரு வாக்காளரும் வெளியிடப்பட்ட இலைகளிலிருந்து தமது சொந்தச் சாட்சியைக் கட்டுகிறார். மாதிரி இரண்டு பாத்திரங்களையும் ஏற்பதால், ஸ்கிரிப்ட் ரகசியங்களின் கோப்பை எடுத்துக்கொள்கிறது.

மாதிரி வாக்கெடுப்பின் ஆழம் 10 (1,024 இலைகள்), வாக்கெடுப்பு id 1, மூன்று தேர்வுகள். அதன் எட்டு உறுப்பினர்களின் ரகசியங்கள் demo-secrets.txt-இல் உள்ளன, வரிக்கு ஒரு 64-hex ரகசியம்; யார் வேண்டுமானாலும் நிரலை அழைக்க முடியும் என்பதற்காக அவை வேண்டுமென்றே பொதுவில் உள்ளன:

$ python3 allowlist.py setup 10 1 3 demo-secrets.txt
root ce8d6ef78a2ccaab688afa50ff969b0f89b23e7f32d8c3746848b46f773f4750
wrote public.txt
$ cat public.txt
10 1 3 4151217614 2882153610 1358596712 261854975 2134815369 1958991922 1874085992 1346846583

முதல் வேர்ச் சொல் 4151217614 என்பது 0xf76e8dce: வேரின் முதல் நான்கு பைட்டுகள் ce 8d 6e f7 லிட்டில்-எண்டியனாகப் படிக்கப்பட்டவை.

5. கட்டமைத்து இயக்குதல்

$ rand-guest build my-guests/allowlist --max-words 65535
427 words against a cap of 65535 (fits); 0 ecall(s) with a non-static a7
OK
wrote my-guests/allowlist/image.bin and its .sha256 (427 words, hc 420876739f0987ae7f0fbd23a98312ed4452b7e77cf6153e424ed02aca7b5afe, program id 93c780859b60516408099dc45247a21aead612f0c65c667248253533dd932bb3)

உறுப்பினர் 5 தேர்வு 2-க்கு வாக்களிக்கிறார். சாட்சி 90 சொற்கள்: ரகசியத்துக்கு 8, குறியெண், தேர்வு, பாதைக்கு 80.

$ cd my-guests/allowlist
$ python3 allowlist.py witness 10 1 demo-secrets.txt 5 2 > witness.txt
nullifier b5516c17d7f51034998f837217777deb8bc90886
nullifier words 392974773 873526743 1921224601 3950868247 2248722827
$ rand-guest run image.bin --public $(cat public.txt) --input $(cat witness.txt) --tier 14
out[0] = 1
out[1] = 2
out[2] = 392974773
out[3] = 873526743
out[4] = 1921224601
out[5] = 3950868247
out[6] = 2248722827
out[7] = 0
cycles 3208
tier 12
tier 14: fits (cycles 3342 of 16383, Poseidon2 permutations 134 of 2048)

ஐந்து நலிஃபையர் சொற்களும், இயந்திரத்துக்கு வெளியே ஸ்கிரிப்ட் கணக்கிட்டவையே. தோல்விகள் ஒரு நிலையுடன் சீராக நிறுத்தப்படுகின்றன, எனவே அவற்றையும் நிரூபிக்க முடியும். ரகசியத்தின் முதல் சொல் மாற்றப்பட்ட அதே சாட்சி out[0] = 2 மட்டுமே தருகிறது; 3-இல் தேர்வு 3-ஐக் கேட்கும் உறுப்பினர் out[0] = 3 பெறுகிறார்.

6. செலவுக் கணக்கு

நிரல்427 சொற்கள்
சுழற்சிகள்ஆழம்-10 மரத்துக்கு 3,208; டைஜஸ்ட் வரிசைகளுடன் 3,342
Keccak வரிசைமாற்றங்கள்12: ஒரு இலை, பத்துக் கணுக்கள், ஒரு நலிஃபையர். ஒரு அழைப்புக்கான உச்சவரம்பு 128
Poseidon2 வரிசைமாற்றங்கள்2,048-இல் 134, அனைத்தும் நிரல், உள்ளீடு, பொது டைஜஸ்ட்கள்
அடுக்கு12

ஆழத்தின் ஒவ்வொரு கூடுதல் நிலைக்கும் ஒரு வரிசைமாற்றமும் ஏறத்தாழ 270 சுழற்சிகளும் செலவாகின்றன. ஆழம்-20 மரம் (பத்து லட்சம் உறுப்பினர்கள்) 5,866 சுழற்சிகளும் 22 வரிசைமாற்றங்களும் என அளவிடப்படுகிறது; அது அடுக்கு 14, ஒரு அழைப்பு பயன்படுத்தக்கூடிய மிக உயர்ந்த அடுக்கு.

சங்கிலி 18 முதல் ஒரு அழைப்பு கேஸுக்குக் கட்டணம் செலுத்துகிறது; Keccak வரிசைமாற்றமே விலையுயர்ந்த அறிவுறுத்தல்: சாதாரண சுழற்சிக்கு 1-க்கு எதிராக 192 கேஸ். ஒரு ஓட்டத்தின் சரியான கேஸையும் அதற்காக அது அறிவிக்கும் வரம்பையும் வாலெட் அச்சிடுகிறது:

gas: 5636 (declaring 6144, tier 12)

ஓட்டத்தின் ஒவ்வொரு வரிசைக்கும் 1 கேஸ் செலவாகிறது; ஒரு Keccak வரிசைக்கு 191 கூடுதல்: 3,344 வரிசைகளுடன் 12 × 191 சேர்த்தால் 5,636; அதில் ஐந்தில் இரண்டு பங்கு பன்னிரண்டு ஹாஷ்கள். rand-guest run எண்ணியதைவிட வாலெட் இரண்டு வரிசைகள் கூடுதலாக எண்ணுகிறது. சங்கிலி 18-இல் ஒரு அழைப்புச் சான்று அதன் பரிவர்த்தனையுடன் பிணைக்கப்படுகிறது; அந்தப் பிணைப்பு பொதுப் பிரிவின் எட்டுக் கூடுதல் சொற்களாகப் பயணிக்கிறது, அது இரண்டு டைஜஸ்ட் வரிசைகளைச் சேர்க்கிறது. அறிவிக்கப்பட்ட வரம்பு 6,144 என்பது சரியான எண்ணை 1,024-இன் மடங்குக்கு மேல்நோக்கி முழுமையாக்கியது. வரம்பு பொதுவானது, சரியான எண் பொதுவானதல்ல.

Keccak அட்டவணையைச் சுமக்கும் சான்று பெரியது. இது 3,346,865 பைட்டுகள்; சங்கிலிகள் 18 முதல் 20 வரை உச்சவரம்பு 4,194,304 (4 MiB). மேலே உள்ள ஆழம்-20 எண்கள் எமுலேட்டர் அளவீடுகள்; இந்தப் பக்கத்துக்காக அந்த ஆழத்தில் எந்தச் சான்றும் உருவாக்கப்படவில்லை.

7. நிறுவுதல்

நிறுவுதல் பொதுப் பிரிவைச் சுமப்பதால், கட்டணம் அதன் 11 சொற்களையும் 427 குறியீட்டுச் சொற்களுடன் சேர்த்து எண்ணுகிறது: 0.001 + 0.0001 × 438 = 0.0448 RAND.

$ rand fee deploy 427 --public-words 11
0.0448 RAND
$ rand program deploy image.bin --public public.txt
program id: ad2f719f12d01c9f3c0962092b9898b75b0914bbb817031d51be13e54c05b970 (427 words, hc 73760842ae87099f23bd0f7fed1283a9e7b752443e15f67c2ad04e42fe5a7bca, public input 11 words, digest 2296cfca8db9d38bd0b7579056c9dea3c8d8005df7b42e2b1e954a75fed67510)
proving the spend authorisation on this machine (tier 10; seconds)…
authorisation proved in 22.9s: tier 10, 1370827 bytes
proving the bundle (tier 14; about a minute and a half on a laptop)…
proved in 190.2s: tier 14, 1497544 bytes
submitted deploy 05f8bfcbf9fccedbd53c3ba41bcc32dc8070ed3433364fc591b4f00699438de6
  0 RAND out, 59.9552 RAND change, fee 0.0448 RAND, anchored at height 97632, proved in 190.2s (bundle) + 22.9s (auth)

deploy அச்சிடும் id ad2f719f…b970, rand-guest build அச்சிட்டது அல்ல. பொதுப் பிரிவு இருக்கும்போது id என்பது blake3("rand-program-2", base_pc ‖ len ‖ words ‖ len ‖ public); எனவே அது வேர், வாக்கெடுப்பு id, தேர்வுகளின் எண்ணிக்கை ஆகியவற்றைப் பிணைக்கிறது. அதே உறுப்பினர்கள் மீதான இரண்டாவது வாக்கெடுப்பு என்பது, வேறு வாக்கெடுப்பு id-உடன் அதே குறியீட்டின் இரண்டாவது நிறுவுதல்; அது வேறு நிரல் id-யையும் வேறு நலிஃபையர்களையும் பெறுகிறது.

hc-உம் வேறுவிதமாக எழுதப்படுகிறது: rand-guest தனது எட்டுச் சொற்களில் ஒவ்வொன்றையும் பிக்-எண்டியனாக அச்சிடுகிறது, வாலெட் ஒவ்வொரு சொல்லின் பைட்டுகளையும் லிட்டில்-எண்டியனாக அச்சிடுகிறது; எனவே 42087673 என்பது 73760842 ஆனது. இது அதே டைஜஸ்ட்.

$ rand program show ad2f719f12d01c9f3c0962092b9898b75b0914bbb817031d51be13e54c05b970
{
  "base_pc": 4096,
  "code_hash": "73760842ae87099f23bd0f7fed1283a9e7b752443e15f67c2ad04e42fe5a7bca",
  "deployed_at": 97821,
  "id": "ad2f719f12d01c9f3c0962092b9898b75b0914bbb817031d51be13e54c05b970",
  "public_digest": "2296cfca8db9d38bd0b7579056c9dea3c8d8005df7b42e2b1e954a75fed67510",
  "public_words_len": 11,
  "words_len": 427
}

பொதுப் பிரிவை யார் வேண்டுமானாலும் திரும்பப் படிக்கலாம். rand_getProgramPublic சொற்களை லிட்டில்-எண்டியன் பைட்டுகளாகத் தருகிறது, எனவே வேர் அச்சிடப்பட்டவாறே தோன்றுகிறது:

$ curl -s https://rpc.randprotocol.org -H 'content-type: application/json' \
    -d '{"jsonrpc":"2.0","id":1,"method":"rand_getProgramPublic","params":["ad2f719f12d01c9f3c0962092b9898b75b0914bbb817031d51be13e54c05b970"]}'
{"id":1,"jsonrpc":"2.0","result":"0a0000000100000003000000ce8d6ef78a2ccaab688afa50ff969b0f89b23e7f32d8c3746848b46f773f4750"}

8. வாக்களித்தல்

rand call ஒவ்வொரு தனிப்பட்ட சொல்லுக்கும் ஒரு --input வீதம், தசம வடிவில், பெறுகிறது. sed சாட்சிக் கோப்பை அந்தக் கொடிகளாக மாற்றுகிறது:

$ rand call ad2f719f12d01c9f3c0962092b9898b75b0914bbb817031d51be13e54c05b970 \
    $(sed 's/[0-9][0-9]*/--input &/g' witness.txt)
proving the call locally (90 inputs stay private, over the program's 11-word public input)…
gas: 5636 (declaring 6144, tier 12)
fee 0.006191437 RAND (incl. two price steps of headroom)
proved in 29.2s: tier 12, 3346865 bytes, outputs [1, 2, 392974773, 873526743, 1921224601, 3950868247, 2248722827, 0]
proving the spend authorisation on this machine (tier 10; seconds)…
authorisation proved in 8.2s: tier 10, 1367303 bytes
proving the bundle (tier 14; about a minute and a half on a laptop)…
proved in 117.2s: tier 14, 1504260 bytes
submitted call 8aa13d1f76d28f63a46c30bb09ef44b1ba35083ca80ba7301997f98e5f783666
  0 RAND out, 59.716708563 RAND change, fee 0.006191437 RAND, anchored at height 98185, proved in 117.2s (bundle) + 8.2s (auth)
{
  "h_in": "ea71e7701e62d49cac37b239bef132178f481835027d62b34da45fd6cf2667c6",
  "h_pub": "2296cfca8db9d38bd0b7579056c9dea3c8d8005df7b42e2b1e954a75fed67510",
  "height": 98335,
  "index": 0,
  "outputs": [
    1,
    2,
    392974773,
    873526743,
    1921224601,
    3950868247,
    2248722827,
    0
  ],
  "program": "ad2f719f12d01c9f3c0962092b9898b75b0914bbb817031d51be13e54c05b970",
  "tier": 12,
  "tx": "8aa13d1f76d28f63a46c30bb09ef44b1ba35083ca80ba7301997f98e5f783666"
}
input transcript published; open it with `rand open-call 8aa13d1f76d28f63a46c30bb09ef44b1ba35083ca80ba7301997f98e5f783666`

சங்கிலி வைத்திருப்பது ரசீதை மட்டுமே: வாக்கெடுப்பின் நிரல் அடையாளத்தின் கீழ், நிலை 1, தேர்வு 2, மற்றும் ஐந்து செல்லாதாக்கிச் சொற்கள். h_pub என்பது வாக்கெடுப்பின் பொதுப் பகுதியின் ஹாஷ் சுருக்கம்; program show அச்சிட்ட அதே மதிப்பு. h_in என்பது 90 தனிப்பட்ட சொற்களுக்கான உப்பிடப்பட்ட உறுதிமொழி; உப்பு இல்லாமல் அது எதையும் வெளிப்படுத்தாது.

Apple Silicon மடிக்கணினியில் இந்த அழைப்பு மூன்று நிமிடங்கள் எடுத்தது, உச்ச நினைவகப் பயன்பாடு 6.1 GB: அழைப்பு நிரூபணத்திற்கு 29 s, செலவு அங்கீகாரத்திற்கு 8 s, கட்டணம் செலுத்தும் தொகுப்பிற்கு 117 s. கட்டணமான 0.0062 RAND பெரும்பாலும் பைட்டுகளுக்கானது. சங்கிலி 20-இல் நான்கு நிரல்கள் பக்கத்தில் அதன் கணக்கீடு உள்ளது.

பதிவு வரி. இயல்பாக, வாலட் தனிப்பட்ட உள்ளீடுகளை அழைப்பாளரின் பார்வைச் சாவி மட்டுமே திறக்கக்கூடிய ஓர் உறையில் முத்திரையிட்டு, அழைப்புடன் வெளியிடுகிறது. அது அழைப்பாளருக்கான பதிவு, வெளிப்படுத்தல் அல்ல: வேறு யாராலும் அதைத் திறக்க முடியாது. எந்தப் பதிவும் வேண்டாத வாக்காளர் --no-envelope கொடுக்கலாம். அதன் பிறகு, வாக்காளர் உட்பட யாராலும், உள்ளீடுகள் என்னவாக இருந்தன என்பதை ஒருபோதும் காட்ட முடியாது.

9. வாக்குகளை எண்ணுதல்

எண்ணுபவருக்குச் சங்கிலியைத் தவிர வேறெதுவும் தேவையில்லை:

  1. வாக்கெடுப்பு. நிரலின் பொதுப் பகுதியைப் பெற்று, வெளியிடப்பட்ட இலைகளின் பட்டியலுடன் வேரைச் சரிபார்க்கவும் (allowlist.py-இன் levels கொண்டு மரத்தை மீண்டும் கட்டவும்). guest நிரலை மூலக் குறியீட்டிலிருந்து மீண்டும் கட்டி, hc-ஐ நிரலின் code_hash-உடன் ஒப்பிடவும்.
  2. ரசீதுகள். program வாக்கெடுப்பின் அடையாளமாகவும், வெளியீடு 0 1 ஆகவும் உள்ள ஒவ்வொரு ரசீதையும் எடுக்கவும். வேறு நிலை கொண்ட ரசீதோ, இடம் 7-இல் 3735928559 கொண்ட ரசீதோ வாக்கு அல்ல.
  3. எண்ணிக்கை. வெளியீடுகள் 2 முதல் 6 வரை அடிப்படையாகக் குழுவாக்கவும். ஒவ்வொரு செல்லாதாக்கியையும், சங்கிலி வரிசையில் அதன் முதல் ரசீதைக் கொண்டு, ஒருமுறை மட்டுமே எண்ணி, அதன் வெளியீடு 1-ஐ எண்ணிக்கையில் சேர்க்கவும்.

எண்ணுபவருக்கு வேலை கொடுக்க மேலும் மூன்று அழைப்புகள் செய்யப்பட்டன. உறுப்பினர் 5 இரண்டாவது முறை, இம்முறை தேர்வு 0-க்கு, வாக்களித்தார். உறுப்பினர் 2 தேர்வு 1-க்கு வாக்களித்தார். உறுப்பினர் 3, பொது RPC முனையத்தை மட்டுமே பயன்படுத்திய ஒரு வாலட்டிலிருந்து, தேர்வு 0-க்கு வாக்களித்தார். உறுப்பினர் 5-இன் இரண்டாவது வாக்கு இது:

$ python3 allowlist.py witness 10 1 demo-secrets.txt 5 0 > witness.txt
nullifier b5516c17d7f51034998f837217777deb8bc90886
nullifier words 392974773 873526743 1921224601 3950868247 2248722827
$ rand call ad2f719f12d01c9f3c0962092b9898b75b0914bbb817031d51be13e54c05b970 \
    $(sed 's/[0-9][0-9]*/--input &/g' witness.txt)
…
proved in 31.0s: tier 12, 3345265 bytes, outputs [1, 0, 392974773, 873526743, 1921224601, 3950868247, 2248722827, 0]
…
submitted call 0f596b87f5137a165d6abc691dbe04f7533db619911d81449fea9ede602c40d0

சங்கிலி இரண்டாவது வாக்கை ஏற்றுக்கொண்டது, ஏற்றுக்கொண்டே ஆக வேண்டும்: நிரூபணம் செல்லுபடியானது. அதன் ரசீது முதல் வாக்கின் அதே ஐந்து செல்லாதாக்கிச் சொற்களைக் கொண்டுள்ளது.

tally.py இந்த மூன்று விதிகளையும் செயல்படுத்துகிறது. அது நிரலின் அழைப்புகளை எக்ஸ்ப்ளோரரிலிருந்தும், ஒவ்வொரு ரசீதையும் பொது RPC-இலிருந்தும் படிக்கிறது; எனவே அதற்கு வாலட்டோ கணுவோ தேவையில்லை:

$ python3 tally.py ad2f719f12d01c9f3c0962092b9898b75b0914bbb817031d51be13e54c05b970
8aa13d1f…  height 98335  choice 2  nullifier b5516c17d7f51034…  counted
0f596b87…  height 99194  choice 0  nullifier b5516c17d7f51034…  repeat, dropped
2db517df…  height 99339  choice 1  nullifier 52f2455918bbc811…  counted
fdbcc960…  height 101833  choice 0  nullifier cd86c5454b809914…  counted
tally: choice 0: 1, choice 1: 1, choice 2: 1

உறுப்பினர் 5-இன் மனமாற்றம் எண்ணப்படவில்லை. அவற்றில் இரண்டு ரசீதுகள் பட்டியலின் ஒரே உறுப்பினரிடமிருந்து வந்தவை என்பதை, செல்லாதாக்கியைத் தவிர, நான்கு ரசீதுகளில் வேறெதுவும் கூறவில்லை. வாக்கெடுப்பு திறந்தே உள்ளது: எட்டு மாதிரி உறுப்பினர்களில் எவராகவும் வாக்களித்து, எண்ணிக்கையை மீண்டும் இயக்கிப் பாருங்கள்.

நிரூபணம் உங்களுக்குத் தராதவை

  • ஒரு ரகசியத்திற்கு ஒரு வாக்கு, ஒரு நபருக்கு ஒரு வாக்கு அல்ல. யாருக்கு இலை கிடைக்கும் என்பதை ஏற்பாட்டாளரே தீர்மானிக்கிறார். இரண்டு இலைகளைப் பதிவு செய்யும் உறுப்பினருக்கு இரண்டு வாக்குகள் உண்டு.
  • வாக்காளரின் ரகசியம் அதன் சீரற்ற தன்மையளவே வலுவானது. ஊகிக்கக்கூடிய சரத்திலிருந்து பெறப்பட்ட ரகசியத்தை, ஊகங்களை ஹாஷ் செய்து வெளியிடப்பட்ட இலைகளுடன் ஒப்பிடுவதன் மூலம் கண்டுபிடிக்கலாம். 32 சீரற்ற பைட்டுகளைப் பயன்படுத்துங்கள்.
  • தேர்வு பொதுவானது. ஒரு செல்லாதாக்கி எந்தத் தேர்வுக்கு வாக்களித்தது என்பதை ரசீது காட்டுகிறது. அந்தச் செல்லாதாக்கி எந்த உறுப்பினர் என்பதை யாராலும் சொல்ல முடியாது; ஆனால் குறைந்த வாக்காளர்களும் வெளியிடப்பட்ட வாக்குப்பதிவு எண்ணிக்கையும் கொண்ட வாக்கெடுப்பு சிறிய எண்களின் புள்ளியியல், குறியாக்கவியல் அல்ல.
  • நேரமும் கட்டணமும் மெட்டாடேட்டா. கட்டணத் தொகுப்பு மறைக்கப்பட்டது, யாரையும் பெயரிடுவதில்லை; ஆனால் அமைதியான நேரத்தில் தன் வாலட்டுக்கு நிதியளித்து ஒரு நிமிடம் கழித்து வாக்களிக்கும் வாக்காளர், கண்காணிப்பவருக்கு ஏதோ ஒன்றைச் சொல்லிவிடுகிறார்.
  • எண்ணிக்கை சங்கிலிக்கு வெளியே நடக்கிறது. சங்கிலி ரசீதுகளைச் சேமிக்கிறது. அவற்றை எண்ணுவதும், இரண்டாவது செல்லாதாக்கியை நிராகரிப்பதும் அவற்றைப் படிப்பவரின் பணி.

fullnode கமிட் 48dc04b (சங்கிலி 18), மற்றும் circuits `aeacf31` (`guest-sdk/src/lib.rs`, `rand-guest`), the commit fullnode v0.6.7 vendors; every `rand-guest` and `rand` output on this page was produced on 2026-09-30, the deploy and the call on chain 18 உடன் 2026-09-30 அன்று இறுதியாகச் சரிபார்க்கப்பட்டது. நிரலும் இந்தப் பக்கமும் முரண்படும் இடங்களில் நிரலே சரியானது; எங்களுக்குத் தெரிவியுங்கள்.