---
title: "தனியுரிமையைப் பாதுகாக்கும் பயன்பாடு சார்ந்த பிளாஸ்மாவை எழுதுங்கள்"
description: "இந்த வழிகாட்டியில், வைப்புகளுக்கான ஒரு பாதி-ரகசிய வங்கியை உருவாக்குகிறோம். வங்கி ஒரு மையப்படுத்தப்பட்ட கூறு; இது ஒவ்வொரு பயனரின் இருப்பையும் அறியும். இருப்பினும், இந்தத் தகவல் சங்கிலிசார் சேமிக்கப்படுவதில்லை. அதற்குப் பதிலாக, வங்கி நிலையின் ஹாஷை வெளியிடுகிறது. ஒவ்வொரு முறை பரிவர்த்தனை நிகழும்போதும், வங்கி புதிய ஹாஷை வெளியிடுகிறது, அத்துடன் ஹாஷ் நிலையைப் புதியதாக மாற்றும் கையொப்பமிடப்பட்ட பரிவர்த்தனை தன்னிடம் உள்ளது என்பதற்கான பூஜ்ஜிய-அறிவுச் சான்றையும் வெளியிடுகிறது. இந்த வழிகாட்டியைப் படித்த பிறகு, பூஜ்ஜிய-அறிவுச் சான்றுகளை எவ்வாறு பயன்படுத்துவது என்பதை மட்டுமல்லாமல், அவற்றை ஏன் பயன்படுத்துகிறீர்கள் மற்றும் அதை எவ்வாறு பாதுகாப்பாகச் செய்வது என்பதையும் நீங்கள் புரிந்துகொள்வீர்கள்."
author: "ஓரி பொமரன்ட்ஸ்"
tags: ["பூஜ்ய-அறிவு", "சேவையகம்", "புறச்சங்கிலி", "தனியுரிமை"]
skill: advanced
breadcrumb: "பயன்பாடு சார்ந்த பிளாஸ்மா"
lang: ta
published: 2025-10-15
---
## அறிமுகம் {#introduction}

[ரோலப்கள்](/developers/docs/scaling/zk-rollups/) என்பதற்கு மாறாக, [பிளாஸ்மாக்கள்](/developers/docs/scaling/plasma) ஒருமைப்பாட்டிற்காக எத்தேரியம் முதன்மை வலைப்பின்னலைப் பயன்படுத்துகின்றன, ஆனால் கிடைக்கும் தன்மைக்காக அல்ல. இந்தக் கட்டுரையில், பிளாஸ்மாவைப் போலச் செயல்படும் ஒரு பயன்பாட்டை எழுதுகிறோம், இதில் எத்திரியம் ஒருமைப்பாட்டிற்கு (அங்கீகரிக்கப்படாத மாற்றங்கள் இல்லை) உத்தரவாதம் அளிக்கிறது, ஆனால் கிடைக்கும் தன்மைக்கு அல்ல (ஒரு மையப்படுத்தப்பட்ட கூறு செயலிழந்து முழு அமைப்பையும் முடக்கலாம்).

நாம் இங்கு எழுதும் பயன்பாடு தனியுரிமையைப் பாதுகாக்கும் ஒரு வங்கியாகும். வெவ்வேறு முகவரிகள் இருப்புகளைக் கொண்ட கணக்குகளைக் கொண்டுள்ளன, மேலும் அவை மற்ற கணக்குகளுக்குப் பணத்தை (ETH) அனுப்பலாம். வங்கி நிலை (கணக்குகள் மற்றும் அவற்றின் இருப்புகள்) மற்றும் பரிவர்த்தனைகளின் ஹாஷ்களை வெளியிடுகிறது, ஆனால் உண்மையான இருப்புகளைப் புறச்சங்கிலியில் தனிப்பட்டதாக வைத்திருக்கக்கூடிய இடத்தில் வைத்திருக்கிறது.

## வடிவமைப்பு {#design}

இது உற்பத்திக்குத் தயாரான அமைப்பு அல்ல, மாறாக ஒரு கற்பித்தல் கருவியாகும். எனவே, இது பல எளிமைப்படுத்தப்பட்ட அனுமானங்களுடன் எழுதப்பட்டுள்ளது.

- நிலையான கணக்குத் தொகுப்பு. ஒரு குறிப்பிட்ட எண்ணிக்கையிலான கணக்குகள் உள்ளன, மேலும் ஒவ்வொரு கணக்கும் முன்னரே தீர்மானிக்கப்பட்ட முகவரிக்குச் சொந்தமானது. பூஜ்ஜிய-அறிவுச் சான்றுகளில் மாறுபடும் அளவிலான தரவுக் கட்டமைப்புகளைக் கையாள்வது கடினம் என்பதால், இது மிகவும் எளிமையான அமைப்பை உருவாக்குகிறது. உற்பத்திக்குத் தயாரான அமைப்புக்கு, நாம் [மெர்கல் வேரை](/developers/tutorials/merkle-proofs-for-offline-data-integrity/) நிலை ஹாஷாகப் பயன்படுத்தலாம் மற்றும் தேவையான நிலுவைகளுக்கான மெர்கல் சான்றுகளை வழங்கலாம்.

- நினைவகச் சேமிப்பகம். ஒரு உற்பத்தி அமைப்பில், மறுதொடக்கம் செய்யப்படும்போது அவற்றைப் பாதுகாப்பதற்காக அனைத்து கணக்கு நிலுவைகளையும் வட்டில் (disk) எழுத வேண்டும். இங்கே, தகவல் வெறுமனே தொலைந்துபோனாலும் பரவாயில்லை.

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

### பூஜ்ஜிய-அறிவுச் சான்றுகள் {#zero-knowledge-proofs}

அடிப்படை அளவில், ஒரு பூஜ்ஜிய-அறிவுச் சான்று, சில பொதுத் தரவுகளான _Data<sub>public</sub>_ மற்றும் _Data<sub>private</sub>_ ஆகியவற்றுக்கு இடையே ஒரு உறவு _Relationship_ இருக்கும் வகையில், நிரூபிப்பவர் சில தரவுகளை, அதாவது _Data<sub>private</sub>_ ஐ அறிந்திருக்கிறார் என்பதைக் காட்டுகிறது. சரிபார்ப்பிக்கு _Relationship_ மற்றும் _Data<sub>public</sub>_ ஆகியவை தெரியும்.

தனியுரிமையைப் பாதுகாக்க, நிலைகள் மற்றும் பரிவர்த்தனைகள் தனிப்பட்டதாக இருக்க வேண்டும். ஆனால் ஒருமைப்பாட்டை உறுதிப்படுத்த, நிலைகளின் [கிரிப்டோகிராஃபிக் ஹாஷ்](https://en.wikipedia.org/wiki/Cryptographic_hash_function) பொதுவானதாக இருக்க வேண்டும். பரிவர்த்தனைகளைச் சமர்ப்பிக்கும் நபர்களுக்கு அந்தப் பரிவர்த்தனைகள் உண்மையில் நடந்தன என்பதை நிரூபிக்க, நாம் பரிவர்த்தனை ஹாஷ்களையும் பதிவிட வேண்டும்.

பெரும்பாலான சந்தர்ப்பங்களில், _Data<sub>private</sub>_ என்பது பூஜ்ஜிய-அறிவுச் சான்று நிரலுக்கான உள்ளீடாகும், மேலும் _Data<sub>public</sub>_ என்பது வெளியீடாகும்.

_Data<sub>private</sub>_ இல் உள்ள இந்தப் புலங்கள்:

- _State<sub>n</sub>_, பழைய நிலை
- _State<sub>n+1</sub>_, புதிய நிலை
- _Transaction_, பழைய நிலையிலிருந்து புதிய நிலைக்கு மாறும் ஒரு பரிவர்த்தனை. இந்தப் பரிவர்த்தனையில் பின்வரும் புலங்கள் இருக்க வேண்டும்:
  - பரிமாற்றத்தைப் பெறும் _இலக்கு முகவரி_
  - பரிமாற்றப்படும் _தொகை_
  - ஒவ்வொரு பரிவர்த்தனையும் ஒரு முறை மட்டுமே செயல்படுத்தப்படுவதை உறுதிசெய்யும் _நான்ஸ்_.
    மூல முகவரி பரிவர்த்தனையில் இருக்க வேண்டிய அவசியமில்லை, ஏனெனில் அதைக் கையொப்பத்திலிருந்து மீட்டெடுக்க முடியும்.
- _Signature_, பரிவர்த்தனையைச் செய்ய அங்கீகரிக்கப்பட்ட ஒரு கையொப்பம். நம்முடைய விஷயத்தில், பரிவர்த்தனையைச் செய்ய அங்கீகரிக்கப்பட்ட ஒரே முகவரி மூல முகவரி மட்டுமே. நமது பூஜ்ய-அறிவு அமைப்பு செயல்படும் விதத்தின் காரணமாக, எத்திரியம் கையொப்பத்துடன் கூடுதலாக, கணக்கின் பொது விசையும் நமக்குத் தேவை.

_Data<sub>public</sub>_ இல் உள்ள புலங்கள் இவை:

- _Hash(State<sub>n</sub>)_ பழைய நிலையின் ஹாஷ்
- _Hash(State<sub>n+1</sub>)_ புதிய நிலையின் ஹாஷ்
- _Hash(Transaction)_ நிலையை _State<sub>n</sub>_ இலிருந்து _State<sub>n+1</sub>_ ஆக மாற்றும் பரிவர்த்தனையின் ஹாஷ்.

இந்த உறவு பல நிபந்தனைகளைச் சரிபார்க்கிறது:

- பொது ஹாஷ்கள் உண்மையில் தனிப்பட்ட புலங்களுக்கான சரியான ஹாஷ்களாகும்.
- பரிவர்த்தனை, பழைய நிலையில் பயன்படுத்தப்படும்போது, புதிய நிலையை உருவாக்குகிறது.
- கையொப்பம் பரிவர்த்தனையின் மூல முகவரியிலிருந்து வருகிறது.

கிரிப்டோகிராஃபிக் ஹாஷ் செயற்கூறுகளின் பண்புகள் காரணமாக, இந்த நிபந்தனைகளை நிரூபிப்பதே ஒருமைப்பாட்டை உறுதிப்படுத்தப் போதுமானது.

### தரவுக் கட்டமைப்புகள் {#data-structures}

சேவையகத்தால் (server) வைத்திருக்கப்படும் நிலையே முதன்மைத் தரவுக் கட்டமைப்பாகும். ஒவ்வொரு கணக்கிற்கும், சேவையகம் கணக்கு நிலுவையையும், [மறுஇயக்கத் தாக்குதல்களைத் (replay attacks)](https://en.wikipedia.org/wiki/Replay_attack) தடுக்கப் பயன்படுத்தப்படும் ஒரு [நான்ஸையும்](https://en.wikipedia.org/wiki/Cryptographic_nonce) கண்காணிக்கிறது.

### கூறுகள் {#components}

இந்த அமைப்புக்கு இரண்டு கூறுகள் தேவை:

- பரிவர்த்தனைகளைப் பெற்று, அவற்றைச் செயல்படுத்தி, பூஜ்ஜிய-அறிவுச் சான்றுகளுடன் சங்கிலியில் ஹாஷ்களைப் பதிவிடும் _சேவையகம்_.
- ஹாஷ்களைச் சேமித்து, நிலை மாற்றங்கள் சட்டபூர்வமானவை என்பதை உறுதிப்படுத்த பூஜ்ஜிய-அறிவுச் சான்றுகளைச் சரிபார்க்கும் ஒரு _திறன் ஒப்பந்தம்_.

### தரவு மற்றும் கட்டுப்பாட்டு ஓட்டம் {#flows}

ஒரு கணக்கிலிருந்து மற்றொரு கணக்கிற்குப் பரிமாற்றம் செய்ய பல்வேறு கூறுகள் தொடர்பு கொள்ளும் வழிகள் இவை.

1. கையொப்பமிட்டவரின் கணக்கிலிருந்து வேறொரு கணக்கிற்குப் பரிமாற்றம் செய்யக் கோரும் கையொப்பமிடப்பட்ட பரிவர்த்தனையை ஒரு வலை உலாவி சமர்ப்பிக்கிறது.

2. பரிவர்த்தனை செல்லுபடியாகும் என்பதைச் சேவையகம் சரிபார்க்கிறது:

   - கையொப்பமிட்டவர் வங்கியில் போதுமான நிலுவையுடன் ஒரு கணக்கைக் கொண்டுள்ளார்.
   - பெறுநர் வங்கியில் ஒரு கணக்கைக் கொண்டுள்ளார்.

3. கையொப்பமிட்டவரின் நிலுவையிலிருந்து பரிமாற்றம் செய்யப்பட்ட தொகையைக் கழித்து, அதைப் பெறுநரின் நிலுவையில் சேர்ப்பதன் மூலம் சேவையகம் புதிய நிலையைக் கணக்கிடுகிறது.

4. நிலை மாற்றம் செல்லுபடியாகும் என்பதற்கான பூஜ்ஜிய-அறிவுச் சான்றைச் சேவையகம் கணக்கிடுகிறது.

5. சேவையகம் எத்திரியத்திற்குப் பின்வருவனவற்றை உள்ளடக்கிய ஒரு பரிவர்த்தனையைச் சமர்ப்பிக்கிறது:

   - புதிய நிலை ஹாஷ்
   - பரிவர்த்தனை ஹாஷ் (இதனால் பரிவர்த்தனையை அனுப்பியவர் அது செயல்படுத்தப்பட்டதை அறிய முடியும்)
   - புதிய நிலைக்கான மாற்றம் செல்லுபடியாகும் என்பதை நிரூபிக்கும் பூஜ்ஜிய-அறிவுச் சான்று

6. திறன் ஒப்பந்தம் பூஜ்ஜிய-அறிவுச் சான்றைச் சரிபார்க்கிறது.

7. பூஜ்ஜிய-அறிவுச் சான்று சரியாக இருந்தால், திறன் ஒப்பந்தம் இந்தச் செயல்களைச் செய்கிறது:
   - தற்போதைய நிலை ஹாஷைப் புதிய நிலை ஹாஷாகப் புதுப்பிக்கிறது
   - புதிய நிலை ஹாஷ் மற்றும் பரிவர்த்தனை ஹாஷுடன் ஒரு பதிவு உள்ளீட்டை (log entry) வெளியிடுகிறது

### கருவிகள் {#tools}

கிளையன்ட் பக்கக் குறியீட்டிற்கு, நாம் [Vite](https://vite.dev/), [React](https://react.dev/), [Viem](https://viem.sh/) மற்றும் [Wagmi](https://wagmi.sh/) ஆகியவற்றைப் பயன்படுத்தப் போகிறோம். இவை தொழில்துறைத் தரத்திலான கருவிகள்; உங்களுக்கு இவற்றில் பரிச்சயம் இல்லை என்றால், நீங்கள் [இந்த வழிகாட்டியைப்](/developers/tutorials/creating-a-wagmi-ui-for-your-contract/) பயன்படுத்தலாம்.

சேவையகத்தின் பெரும்பகுதி [Node](https://nodejs.org/en) ஐப் பயன்படுத்தி JavaScript இல் எழுதப்பட்டுள்ளது. பூஜ்ய-அறிவுப் பகுதி [Noir](https://noir-lang.org/) இல் எழுதப்பட்டுள்ளது. நமக்கு `1.0.0-beta.10` பதிப்பு தேவை, எனவே நீங்கள் [அறிவுறுத்தப்பட்டபடி Noir ஐ நிறுவிய](https://noir-lang.org/docs/getting_started/quick_start) பிறகு, இதை இயக்கவும்:

```
noirup -v 1.0.0-beta.10
```

நாம் பயன்படுத்தும் தொகுதிச்சங்கிலி `anvil` ஆகும், இது [Foundry](https://getfoundry.sh/introduction/installation) இன் ஒரு பகுதியாக இருக்கும் உள்ளூர்ச் சோதனைத் தொகுதிச்சங்கிலியாகும்.

## செயலாக்கம் {#implementation}

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

### கட்டம் 1 - கைமுறை பூஜ்ய-அறிவு {#stage-1}

முதல் கட்டத்திற்கு, நாம் உலாவியில் ஒரு பரிவர்த்தனையில் கையொப்பமிட்டு, பின்னர் அந்தத் தகவலைப் பூஜ்ஜிய-அறிவுச் சான்றுக்கு கைமுறையாக வழங்குவோம். பூஜ்ய-அறிவு குறியீடு அந்தத் தகவலை `server/noir/Prover.toml` இல் பெற எதிர்பார்க்கிறது ([இங்கே](https://noir-lang.org/docs/getting_started/project_breakdown#provertoml-1) ஆவணப்படுத்தப்பட்டுள்ளது).

இதைச் செயல்பாட்டில் காண:

1. உங்களிடம் [Node](https://nodejs.org/en/download) மற்றும் [Noir](https://noir-lang.org/install) நிறுவப்பட்டுள்ளதா என்பதை உறுதிப்படுத்திக் கொள்ளுங்கள். முன்னுரிமையாக, அவற்றை macOS, Linux அல்லது [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) போன்ற UNIX அமைப்பில் நிறுவவும்.

2. கட்டம் 1 குறியீட்டைப் பதிவிறக்கி, கிளையன்ட் குறியீட்டை வழங்க வலைச் சேவையகத்தைத் தொடங்கவும்.

   ```sh
   git clone https://github.com/qbzzt/250911-zk-bank.git -b 01-manual-zk
   cd 250911-zk-bank
   cd client
   npm install
   npm run dev
   ```

   இங்கே உங்களுக்கு ஒரு வலைச் சேவையகம் தேவைப்படுவதற்குக் காரணம், சில வகையான மோசடிகளைத் தடுக்க, பல பணப்பைகள் (மெட்டாமேஸ்க் போன்றவை) வட்டில் இருந்து நேரடியாக வழங்கப்படும் கோப்புகளை ஏற்றுக்கொள்வதில்லை

3. பணப்பையுடன் ஒரு உலாவியைத் திறக்கவும்.

4. பணப்பையில், புதிய கடவுச்சொற்றொடரை உள்ளிடவும். இது உங்களின் தற்போதைய கடவுச்சொற்றொடரை நீக்கிவிடும் என்பதை நினைவில் கொள்ளவும், எனவே _உங்களிடம் காப்புப்பிரதி இருப்பதை உறுதிப்படுத்திக் கொள்ளுங்கள்_.

   கடவுச்சொற்றொடர் `test test test test test test test test test test test junk` ஆகும், இது anvil-க்கான இயல்புநிலை சோதனைக் கடவுச்சொற்றொடராகும்.

5. [கிளையன்ட் பக்கக் குறியீட்டிற்கு](http://localhost:5173/) உலாவவும்.

6. பணப்பையுடன் இணைத்து, உங்கள் இலக்குக் கணக்கு மற்றும் தொகையைத் தேர்ந்தெடுக்கவும்.

7. **Sign** என்பதைக் கிளிக் செய்து பரிவர்த்தனையில் கையொப்பமிடுங்கள்.

8. **Prover.toml** தலைப்பின் கீழ், நீங்கள் உரையைக் காண்பீர்கள். `server/noir/Prover.toml` என்பதை அந்த உரையைக் கொண்டு மாற்றவும்.

9. பூஜ்ஜிய-அறிவுச் சான்றைச் செயல்படுத்தவும்.

   ```sh
   cd ../server/noir
   nargo execute
   ```

   வெளியீடு இதைப் போலவே இருக்க வேண்டும்

      ```
ori@CryptoDocGuy:~/noir/250911-zk-bank/server/noir$ nargo execute

   [zkBank] Circuit witness successfully solved
   [zkBank] Witness saved to target/zkBank.gz
   [zkBank] Circuit output: (0x199aa62af8c1d562a6ec96e66347bf3240ab2afb5d022c895e6bf6a5e617167b, 0x0cfc0a67cb7308e4e9b254026b54204e34f6c8b041be207e64c5db77d95dd82d, 0x450cf9da6e180d6159290554ae3d8787, 0x6d8bc5a15b9037e52fb59b6b98722a85)
      ```

10. செய்தி சரியாக ஹாஷ் செய்யப்பட்டுள்ளதா என்பதைப் பார்க்க, கடைசி இரண்டு மதிப்புகளை வலை உலாவியில் நீங்கள் காணும் ஹாஷுடன் ஒப்பிடவும்.

#### `server/noir/Prover.toml` {#server-noir-prover-toml}

[இந்தக் கோப்பு](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/Prover.toml) Noir எதிர்பார்க்கும் தகவல் வடிவமைப்பைக் காட்டுகிறது.

```toml
message="send 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 500 finney (milliEth) 0                             "
```

செய்தி உரை வடிவமைப்பில் உள்ளது, இது பயனருக்குப் புரிந்துகொள்வதை எளிதாக்குகிறது (கையொப்பமிடும்போது இது அவசியம்) மற்றும் Noir குறியீட்டைப் பாகுபடுத்துவதையும் எளிதாக்குகிறது. ஒருபுறம் பின்னப் பரிமாற்றங்களைச் செயல்படுத்துவதற்கும், மறுபுறம் எளிதாகப் படிக்கக்கூடியதாக இருப்பதற்கும் தொகை ஃபின்னி-களில் குறிப்பிடப்பட்டுள்ளது. கடைசி எண் [நான்ஸ்](https://en.wikipedia.org/wiki/Cryptographic_nonce) ஆகும்.

சரம் 100 எழுத்துகள் நீளமானது. பூஜ்ஜிய-அறிவுச் சான்றுகள் மாறுபடும் அளவுள்ள தரவைச் சரியாகக் கையாளுவதில்லை, எனவே தரவை நிரப்புவது (pad) பெரும்பாலும் அவசியமாகிறது.

```toml
pubKeyX=["0x83",...,"0x75"]
pubKeyY=["0x35",...,"0xa5"]
signature=["0xb1",...,"0x0d"]
```

இந்த மூன்று அளவுருக்களும் நிலையான அளவுள்ள பைட் வரிசைகளாகும்.

```toml
[[accounts]]
address="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
balance=100_000
nonce=0

[[accounts]]
address="0x70997970C51812dc3A010C7d01b50e0d17dc79C8"
balance=100_000
nonce=0
```

கட்டமைப்புகளின் வரிசையைக் குறிப்பிடுவதற்கான வழி இதுதான். ஒவ்வொரு உள்ளீட்டிற்கும், முகவரி, இருப்பு (milliETH அல்லது [ஃபின்னி](https://cryptovalleyjournal.com/glossary/finney/) இல்) மற்றும் அடுத்த நான்ஸ் மதிப்பைக் குறிப்பிடுகிறோம்.

#### `client/src/Transfer.tsx` {#client-src-transfer-tsx}

[இந்தக் கோப்பு](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/client/src/Transfer.tsx) கிளையன்ட் பக்கச் செயலாக்கத்தைச் செயல்படுத்துகிறது மற்றும் `server/noir/Prover.toml` கோப்பை உருவாக்குகிறது (பூஜ்ய-அறிவு அளவுருக்களை உள்ளடக்கியது).

மிகவும் சுவாரஸ்யமான பகுதிகளின் விளக்கம் இங்கே.

```tsx
export default attrs =>  {
```

இந்தச் செயற்கூறு `Transfer` React கூறினை உருவாக்குகிறது, இதை மற்ற கோப்புகள் இறக்குமதி செய்யலாம்.

```tsx
  const accounts = [
    "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
    "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
    "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC",
    "0x90F79bf6EB2c4f870365E785982E1f101E93b906",
    "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65",
  ]
```

இவை கணக்கு முகவரிகள், `test ... test junk` கடவுச்சொற்றொடரால் உருவாக்கப்பட்ட முகவரிகள். உங்கள் சொந்த முகவரிகளைப் பயன்படுத்த விரும்பினால், இந்த வரையறையை மாற்றியமைக்கவும்.

```tsx
  const account = useAccount()
  const wallet = createWalletClient({
    transport: custom(window.ethereum!)
  })
```

இந்த [Wagmi கொக்கிகள் (hooks)](https://wagmi.sh/react/api/hooks) [Viem](https://viem.sh/) நிரலகம் மற்றும் பணப்பையை அணுக அனுமதிக்கின்றன.

```tsx
  const message = `send ${toAccount} ${ethAmount*1000} finney (milliEth) ${nonce}`.padEnd(100, " ")
```

இது இடைவெளிகளால் நிரப்பப்பட்ட செய்தியாகும். [`useState`](https://react.dev/reference/react/useState) மாறிகளில் ஒன்று மாறும் ஒவ்வொரு முறையும், கூறு மீண்டும் வரையப்பட்டு `message` புதுப்பிக்கப்படும்.

```tsx
  const sign = async () => {
```

பயனர் **Sign** பொத்தானைக் கிளிக் செய்யும்போது இந்தச் செயற்கூறு அழைக்கப்படுகிறது. செய்தி தானாகவே புதுப்பிக்கப்படும், ஆனால் கையொப்பத்திற்குப் பணப்பையில் பயனர் ஒப்புதல் தேவை, மேலும் தேவைப்படாவிட்டால் அதைக் கேட்க நாங்கள் விரும்பவில்லை.

```tsx
    const signature = await wallet.signMessage({
        account: fromAccount,
        message,
    })
```

[செய்தியில் கையொப்பமிட](https://viem.sh/docs/accounts/local/signMessage) பணப்பையைக் கேட்கவும். 

```tsx
    const hash = hashMessage(message)
```

செய்தி ஹாஷைப் பெறவும். பிழைத்திருத்தத்திற்காக (Noir குறியீட்டின்) பயனருக்கு இதை வழங்குவது உதவியாக இருக்கும். 

```tsx
    const pubKey = await recoverPublicKey({
        hash,
        signature
    })
```

[பொது விசையைப் பெறவும்](https://viem.sh/docs/utilities/recoverPublicKey). [Noir `ecrecover`](https://github.com/colinnielsen/ecrecover-noir) செயற்கூறுக்கு இது தேவை.

```tsx
    setSignature(signature)
    setHash(hash)
    setPubKey(pubKey)
```

நிலை மாறிகளை அமைக்கவும். இதைச் செய்வது கூறினை மீண்டும் வரைகிறது (`sign` செயற்கூறு வெளியேறிய பிறகு) மற்றும் புதுப்பிக்கப்பட்ட மதிப்புகளைப் பயனருக்குக் காட்டுகிறது.

```tsx
    let proverToml = `
```

`Prover.toml` க்கான உரை.

```tsx
message="${message}"

pubKeyX=${hexToArray(pubKey.slice(4,4+2*32))}
pubKeyY=${hexToArray(pubKey.slice(4+2*32))}
```

Viem பொது விசையை 65-பைட் ஹெக்ஸாடெசிமல் சரமாக நமக்கு வழங்குகிறது. முதல் பைட் `0x04` ஆகும், இது ஒரு பதிப்புக் குறிப்பான். இதைத் தொடர்ந்து பொது விசையின் `x` க்கான 32 பைட்டுகளும், பின்னர் பொது விசையின் `y` க்கான 32 பைட்டுகளும் உள்ளன.

இருப்பினும், Noir இந்தத் தகவலை இரண்டு-பைட் வரிசைகளாகப் பெற எதிர்பார்க்கிறது, ஒன்று `x` க்கும் மற்றொன்று `y` க்கும். பூஜ்ஜிய-அறிவுச் சான்றின் ஒரு பகுதியாகப் பாகுபடுத்துவதை விட, கிளையன்டில் இங்கே பாகுபடுத்துவது எளிது.

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

```tsx
signature=${hexToArray(signature.slice(2,-2))}
```

கையொப்பமும் 65-பைட் ஹெக்ஸாடெசிமல் சரமாக வழங்கப்படுகிறது. இருப்பினும், பொது விசையை மீட்டெடுக்க மட்டுமே கடைசி பைட் அவசியமாகும். பொது விசை ஏற்கனவே Noir குறியீட்டிற்கு வழங்கப்படும் என்பதால், கையொப்பத்தைச் சரிபார்க்க அது நமக்குத் தேவையில்லை, மேலும் Noir குறியீட்டிற்கும் அது தேவையில்லை.

```tsx
${accounts.map(accountInProverToml).reduce((a,b) => a+b, "")}
`
```

கணக்குகளை வழங்கவும்.

```tsx
    setProverToml(proverToml)
  }

  return (
    <>
        <h2>Transfer</h2>
```

இது கூறின் HTML (மிகத் துல்லியமாக, [JSX](https://react.dev/learn/writing-markup-with-jsx)) வடிவமைப்பாகும்.

#### `server/noir/src/main.nr` {#server-noir-src-main-nr}

[இந்தக் கோப்பு](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/src/main.nr) உண்மையான பூஜ்ய-அறிவுக் குறியீடாகும்.

```
use std::hash::pedersen_hash;
```

[Pedersen ஹாஷ்](https://rya-sge.github.io/access-denied/2024/05/07/pedersen-hash-function/) [Noir நிலையான நிரலகத்துடன்](https://noir-lang.org/docs/noir/standard_library/cryptographic_primitives/hashes#pedersen_hash) வழங்கப்படுகிறது. பூஜ்ஜிய-அறிவுச் சான்றுகள் பொதுவாக இந்த ஹாஷ் செயற்கூற்றைப் பயன்படுத்துகின்றன. நிலையான ஹாஷ் செயற்கூறுகளுடன் ஒப்பிடும்போது [எண்கணிதச் சுற்றுகளுக்குள் (arithmetic circuits)](https://rareskills.io/post/arithmetic-circuit) கணக்கிடுவது மிகவும் எளிதானது.

```
use keccak256::keccak256;
use dep::ecrecover;
```

இந்த இரண்டு செயற்கூறுகளும் [`Nargo.toml`](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/Nargo.toml) இல் வரையறுக்கப்பட்ட வெளிப்புற நிரலகங்களாகும். அவை அவற்றின் பெயருக்கு ஏற்றவாறு சரியாகச் செயல்படுகின்றன, ஒன்று [keccak256 ஹாஷைக்](https://emn178.github.io/online-tools/keccak_256.html) கணக்கிடும் செயற்கூறு, மற்றொன்று எத்திரியம் கையொப்பங்களைச் சரிபார்த்து கையொப்பமிட்டவரின் எத்திரியம் முகவரியை மீட்டெடுக்கும் செயற்கூறு.

```
global ACCOUNT_NUMBER : u32 = 5;
```

Noir [Rust](https://www.rust-lang.org/) இலிருந்து ஈர்க்கப்பட்டது. மாறிகள், இயல்புநிலையாக, மாறிலிகளாகும். உலகளாவிய உள்ளமைவு மாறிலிகளை நாம் இப்படித்தான் வரையறுக்கிறோம். குறிப்பாக, `ACCOUNT_NUMBER` என்பது நாம் சேமிக்கும் கணக்குகளின் எண்ணிக்கையாகும்.

`u<number>` எனப் பெயரிடப்பட்ட தரவு வகைகள் அந்த எண்ணிக்கையிலான பிட்களைக் கொண்ட, குறியிடப்படாத (unsigned) வகைகளாகும். ஆதரிக்கப்படும் ஒரே வகைகள் `u8`, `u16`, `u32`, `u64` மற்றும் `u128` மட்டுமே.

```
global FLAT_ACCOUNT_FIELDS : u32 = 2;
```

கீழே விளக்கப்பட்டுள்ளபடி, கணக்குகளின் Pedersen ஹாஷுக்கு இந்த மாறி பயன்படுத்தப்படுகிறது.

```
global MESSAGE_LENGTH : u32 = 100;
```

மேலே விளக்கியபடி, செய்தியின் நீளம் நிலையானது. அது இங்கே குறிப்பிடப்பட்டுள்ளது.

```
global ASCII_MESSAGE_LENGTH : [u8; 3] = [0x31, 0x30, 0x30];
global HASH_BUFFER_SIZE : u32 = 26+3+MESSAGE_LENGTH;
```

[EIP-191 கையொப்பங்களுக்கு](https://eips.ethereum.org/EIPS/eip-191) 26-பைட் முன்னொட்டுடன் கூடிய இடையகம் (buffer) தேவை, அதைத் தொடர்ந்து ASCII இல் செய்தியின் நீளம், இறுதியாகச் செய்தி ஆகியவை தேவை.

```
struct Account {
    balance: u128,
    address: Field,
    nonce: u32,
}
```

ஒரு கணக்கைப் பற்றி நாம் சேமிக்கும் தகவல். [`Field`](https://noir-lang.org/docs/noir/concepts/data_types/fields) என்பது ஒரு எண், பொதுவாக 253 பிட்கள் வரை இருக்கும், இது பூஜ்ஜிய-அறிவுச் சான்றைச் செயல்படுத்தும் [எண்கணிதச் சுற்றில்](https://rareskills.io/post/arithmetic-circuit) நேரடியாகப் பயன்படுத்தப்படலாம். இங்கே 160-பிட் எத்திரியம் முகவரியைச் சேமிக்க `Field` ஐப் பயன்படுத்துகிறோம்.

```
struct TransferTxn {
    from: Field,
    to: Field,
    amount: u128,
    nonce: u32
}
```

பரிமாற்றப் பரிவர்த்தனைக்காக நாம் சேமிக்கும் தகவல்.

```
fn flatten_account(account: Account) -> [Field; FLAT_ACCOUNT_FIELDS] {
```

ஒரு செயற்கூறு வரையறை. அளவுரு என்பது `Account` தகவலாகும். இதன் முடிவு `Field` மாறிகளின் வரிசையாகும், இதன் நீளம் `FLAT_ACCOUNT_FIELDS` ஆகும்.

```
let flat = [
        account.address,
        ((account.balance << 32) + account.nonce.into()).into(),
    ];
```

வரிசையில் உள்ள முதல் மதிப்பு கணக்கு முகவரியாகும். இரண்டாவது இருப்பு மற்றும் நான்ஸ் இரண்டையும் உள்ளடக்கியது. `.into()` அழைப்புகள் ஒரு எண்ணை அதற்குத் தேவையான தரவு வகையாக மாற்றுகின்றன. `account.nonce` என்பது ஒரு `u32` மதிப்பாகும், ஆனால் அதை `account.balance << 32` என்ற `u128` மதிப்புடன் சேர்க்க, அது ஒரு `u128` ஆக இருக்க வேண்டும். அதுதான் முதல் `.into()`. இரண்டாவது `u128` முடிவை `Field` ஆக மாற்றுகிறது, இதனால் அது வரிசையில் பொருந்தும்.

```
flat
}
```

Noir இல், செயற்கூறுகள் முடிவில் மட்டுமே ஒரு மதிப்பை வழங்க முடியும் (முன்கூட்டியே திரும்பும் வசதி இல்லை). திரும்பும் மதிப்பைக் குறிப்பிட, செயற்கூறின் இறுதி அடைப்புக்குறிக்கு சற்று முன்பு அதை மதிப்பிடுகிறீர்கள்.

```
fn flatten_accounts(accounts: [Account; ACCOUNT_NUMBER]) -> [Field; FLAT_ACCOUNT_FIELDS*ACCOUNT_NUMBER] {
```

இந்தச் செயற்கூறு கணக்குகளின் வரிசையை `Field` வரிசையாக மாற்றுகிறது, இது Petersen ஹாஷுக்கான உள்ளீடாகப் பயன்படுத்தப்படலாம்.

```
let mut flat: [Field; FLAT_ACCOUNT_FIELDS*ACCOUNT_NUMBER] = [0; FLAT_ACCOUNT_FIELDS*ACCOUNT_NUMBER];
```

மாற்றக்கூடிய மாறியை, அதாவது மாறிலி _அல்லாததை_ நீங்கள் இப்படித்தான் குறிப்பிடுகிறீர்கள். Noir இல் உள்ள மாறிகள் எப்போதும் ஒரு மதிப்பைக் கொண்டிருக்க வேண்டும், எனவே இந்த மாறியை நாம் முழுவதும் பூஜ்ஜியங்களாகத் துவக்குகிறோம்.

```
for i in 0..ACCOUNT_NUMBER {
```

இது ஒரு `for` மடக்கியாகும் (loop). எல்லைகள் மாறிலிகள் என்பதை நினைவில் கொள்ளவும். Noir மடக்கிகள் தொகுக்கும் நேரத்தில் (compile time) அவற்றின் எல்லைகளை அறிந்திருக்க வேண்டும். இதற்குக் காரணம், எண்கணிதச் சுற்றுகள் பாய்வுக் கட்டுப்பாட்டை (flow control) ஆதரிப்பதில்லை. ஒரு `for` மடக்கியைச் செயலாக்கும்போது, தொகுப்பி (compiler) அதனுள் உள்ள குறியீட்டைப் பல முறை, ஒவ்வொரு சுழற்சிக்கும் (iteration) ஒன்றாக வைக்கிறது.

```
let fields = flatten_account(accounts[i]);
        for j in 0..FLAT_ACCOUNT_FIELDS {
            flat[i*FLAT_ACCOUNT_FIELDS + j] = fields[j];
        }
    }

    flat
}

fn hash_accounts(accounts: [Account; ACCOUNT_NUMBER]) -> Field {
    pedersen_hash(flatten_accounts(accounts))
}
```

இறுதியாக, கணக்குகளின் வரிசையை ஹாஷ் செய்யும் செயற்கூற்றுக்கு வந்தோம்.

```
fn find_account(accounts: [Account; ACCOUNT_NUMBER], address: Field) -> u32 {
    let mut account : u32 = ACCOUNT_NUMBER;

    for i in 0..ACCOUNT_NUMBER {
        if accounts[i].address == address {
            account = i;
        }
    }
```

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

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

`if` கூற்றுகளிலும் இதே போன்ற ஒன்று நிகழ்கிறது. மேலே உள்ள மடக்கியில் உள்ள `if` கூற்று இந்த கணிதக் கூற்றுகளாக மொழிபெயர்க்கப்பட்டுள்ளது.

_condition<sub>result</sub> = accounts[i].address == address_ // அவை சமமாக இருந்தால் ஒன்று, இல்லையெனில் பூஜ்ஜியம்

_account<sub>new</sub> = condition<sub>result</sub>\*i + (1-condition<sub>result</sub>)\*account<sub>old</sub>_

```rust
    assert (account < ACCOUNT_NUMBER, f"{address} does not have an account");

    account
}
```

[`assert`](https://noir-lang.org/docs/dev/noir/concepts/assert) செயற்கூறு, வலியுறுத்தல் (assertion) தவறாக இருந்தால் பூஜ்ஜிய-அறிவுச் சான்றைச் செயலிழக்கச் செய்கிறது. இந்த நிலையில், தொடர்புடைய முகவரியுடன் ஒரு கணக்கை நம்மால் கண்டுபிடிக்க முடியாவிட்டால். முகவரியைப் புகாரளிக்க, நாம் ஒரு [வடிவமைப்புச் சரத்தைப் (format string)](https://noir-lang.org/docs/noir/concepts/data_types/strings#format-strings) பயன்படுத்துகிறோம்.

```rust
fn apply_transfer_txn(accounts: [Account; ACCOUNT_NUMBER], txn: TransferTxn) -> [Account; ACCOUNT_NUMBER] {
```

இந்தச் செயற்கூறு ஒரு பரிமாற்றப் பரிவர்த்தனையைப் பயன்படுத்துகிறது மற்றும் புதிய கணக்குகளின் வரிசையை வழங்குகிறது.

```rust
    let from = find_account(accounts, txn.from);
    let to = find_account(accounts, txn.to);

    let (txnFrom, txnAmount, txnNonce, accountNonce) =
        (txn.from, txn.amount, txn.nonce, accounts[from].nonce);
```

Noir இல் உள்ள வடிவமைப்புச் சரத்திற்குள் கட்டமைப்பு உறுப்புகளை நம்மால் அணுக முடியாது, எனவே நாம் பயன்படுத்தக்கூடிய நகலை உருவாக்குகிறோம்.

```rust
    assert (accounts[from].balance >= txn.amount,
        f"{txnFrom} does not have {txnAmount} finney");

    assert (accounts[from].nonce == txn.nonce,
        f"Transaction has nonce {txnNonce}, but the account is expected to use {accountNonce}");
```

இவை ஒரு பரிவர்த்தனையைச் செல்லாததாக்கக்கூடிய இரண்டு நிபந்தனைகளாகும்.

```rust
    let mut newAccounts = accounts;

    newAccounts[from].balance -= txn.amount;
    newAccounts[from].nonce += 1;
    newAccounts[to].balance += txn.amount;

    newAccounts
}
```

புதிய கணக்குகளின் வரிசையை உருவாக்கி, பின்னர் அதை வழங்கவும்.

```rust
fn readAddress(messageBytes: [u8; MESSAGE_LENGTH]) -> Field
```

இந்தச் செயற்கூறு செய்தியிலிருந்து முகவரியைப் படிக்கிறது. 

```rust
{
    let mut result : Field = 0;

    for i in 7..47 {
```

முகவரி எப்போதும் 20 பைட்டுகள் (அதாவது 40 ஹெக்ஸாடெசிமல் இலக்கங்கள்) நீளமானது, மேலும் இது எழுத்து #7 இல் தொடங்குகிறது.

```rust
        result *= 0x10;
        if messageBytes[i] >= 48 & messageBytes[i] <= 57 {    // 0-9
            result += (messageBytes[i]-48).into();
        }
        if messageBytes[i] >= 65 & messageBytes[i] <= 70 {    // A-F
            result += (messageBytes[i]-65+10).into()
        }
        if messageBytes[i] >= 97 & messageBytes[i] <= 102 {   // a-f
            result += (messageBytes[i]-97+10).into()
        }        
    }    

    result
}

fn readAmountAndNonce(messageBytes: [u8; MESSAGE_LENGTH]) -> (u128, u32)
```

செய்தியிலிருந்து தொகை மற்றும் நான்ஸைப் படிக்கவும். 

```rust
{
    let mut amount : u128 = 0;
    let mut nonce: u32 = 0;
    let mut stillReadingAmount: bool = true;
    let mut lookingForNonce: bool = false;
    let mut stillReadingNonce: bool = false;
```

செய்தியில், முகவரிக்குப் பிறகு வரும் முதல் எண் பரிமாற்றம் செய்ய வேண்டிய ஃபின்னி-யின் (அதாவது ETH இன் ஆயிரத்தில் ஒரு பங்கு) தொகையாகும். இரண்டாவது எண் நான்ஸ் ஆகும். அவற்றுக்கிடையேயான எந்த உரையும் புறக்கணிக்கப்படும்.

```rust
    for i in 48..MESSAGE_LENGTH {
        if messageBytes[i] >= 48 & messageBytes[i] <= 57 {    // 0-9
            let digit = (messageBytes[i]-48);

            if stillReadingAmount {
                amount = amount*10 + digit.into();
            }

            if lookingForNonce {    // நாங்கள் அதை இப்போதுதான் கண்டறிந்தோம்
                stillReadingNonce = true;
                lookingForNonce = false;
            }

            if stillReadingNonce {
                nonce = nonce*10 + digit.into();
            }
        } else {
            if stillReadingAmount {
                stillReadingAmount = false;
                lookingForNonce = true;
            }
            if stillReadingNonce {
                stillReadingNonce = false;
            }
        }
    }

    (amount, nonce)
}
```

ஒரு [டூப்பிளை (tuple)](https://noir-lang.org/docs/noir/concepts/data_types/tuples) வழங்குவது என்பது ஒரு செயற்கூறிலிருந்து பல மதிப்புகளை வழங்குவதற்கான Noir வழியாகும்.

```rust
fn readTransferTxn(message: str<MESSAGE_LENGTH>) -> TransferTxn 
{
    let mut txn: TransferTxn = TransferTxn { from: 0, to: 0, amount:0, nonce:0 };
    let messageBytes = message.as_bytes();

    txn.to = readAddress(messageBytes);
    let (amount, nonce) = readAmountAndNonce(messageBytes);
    txn.amount = amount;
    txn.nonce = nonce;

    txn
}
```

இந்தச் செயற்கூறு செய்தியை பைட்டுகளாக மாற்றுகிறது, பின்னர் தொகைகளை `TransferTxn` ஆக மாற்றுகிறது.

```rust
// Viem இன் hashMessage-க்கு இணையானது
// https://viem.sh/docs/utilities/hashMessage#hashmessage
fn hashMessage(message: str<MESSAGE_LENGTH>) -> [u8;32] {
```

கணக்குகள் பூஜ்ஜிய-அறிவுச் சான்றுக்குள் மட்டுமே ஹாஷ் செய்யப்படுவதால், கணக்குகளுக்கு Pedersen ஹாஷைப் பயன்படுத்த முடிந்தது. இருப்பினும், இந்தக் குறியீட்டில் உலாவியால் உருவாக்கப்பட்ட செய்தியின் கையொப்பத்தை நாம் சரிபார்க்க வேண்டும். அதற்கு, [EIP-191](https://eips.ethereum.org/EIPS/eip-191) இல் உள்ள எத்திரியம் கையொப்பமிடும் வடிவமைப்பைப் பின்பற்ற வேண்டும். அதாவது, நிலையான முன்னொட்டு, ASCII இல் செய்தியின் நீளம் மற்றும் செய்தி ஆகியவற்றுடன் ஒரு ஒருங்கிணைந்த இடையகத்தை உருவாக்கி, அதை ஹாஷ் செய்ய எத்திரியம் நிலையான keccak256 ஐப் பயன்படுத்த வேண்டும்.

```rust
    // ASCII முன்னொட்டு
    let prefix_bytes = [
        0x19, // \x19
        0x45, // 'E'
        0x74, // 't'
        0x68, // 'h'
        0x65, // 'e'
        0x72, // 'r'
        0x65, // 'e'
        0x75, // 'u'
        0x6D, // 'm'
        0x20, // ' '
        0x53, // 'S'
        0x69, // 'i'
        0x67, // 'g'
        0x6E, // 'n'
        0x65, // 'e'
        0x64, // 'd'
        0x20, // ' '
        0x4D, // 'M'
        0x65, // 'e'
        0x73, // 's'
        0x73, // 's'
        0x61, // 'a'
        0x67, // 'g'
        0x65, // 'e'
        0x3A, // ':'
        0x0A  // '\n'
    ];
```

ஒரு பரிவர்த்தனையாகவோ அல்லது வேறு சில நோக்கங்களுக்காகவோ பயன்படுத்தக்கூடிய ஒரு செய்தியில் கையொப்பமிடுமாறு ஒரு பயன்பாடு பயனரைக் கேட்கும் நிகழ்வுகளைத் தவிர்க்க, EIP-191 அனைத்துக் கையொப்பமிடப்பட்ட செய்திகளும் 0x19 (செல்லுபடியாகும் ASCII எழுத்து அல்ல) என்ற எழுத்துடன் தொடங்க வேண்டும் என்றும், அதைத் தொடர்ந்து `Ethereum Signed Message:` மற்றும் ஒரு புதிய வரி இருக்க வேண்டும் என்றும் குறிப்பிடுகிறது.

```rust
    let mut buffer: [u8; HASH_BUFFER_SIZE] = [0u8; HASH_BUFFER_SIZE];
    for i in 0..26 {
        buffer[i] = prefix_bytes[i];
    }

    let messageBytes : [u8; MESSAGE_LENGTH] = message.as_bytes();

    if MESSAGE_LENGTH <= 9 {
        for i in 0..1 {
            buffer[i+26] = ASCII_MESSAGE_LENGTH[i];
        }

        for i in 0..MESSAGE_LENGTH {
            buffer[i+26+1] = messageBytes[i];
        }
    }

    if MESSAGE_LENGTH >= 10 & MESSAGE_LENGTH <= 99 {
        for i in 0..2 {
            buffer[i+26] = ASCII_MESSAGE_LENGTH[i];
        }

        for i in 0..MESSAGE_LENGTH {
            buffer[i+26+2] = messageBytes[i];
        }
    }

    if MESSAGE_LENGTH >= 100 {
        for i in 0..3 {
            buffer[i+26] = ASCII_MESSAGE_LENGTH[i];
        }

        for i in 0..MESSAGE_LENGTH {
            buffer[i+26+3] = messageBytes[i];
        }
    }

    assert(MESSAGE_LENGTH < 1000, "Messages whose length is over three digits are not supported");
```

999 வரையிலான செய்தி நீளங்களைக் கையாளவும், அது அதிகமாக இருந்தால் தோல்வியடையச் செய்யவும். செய்தியின் நீளம் ஒரு மாறிலியாக இருந்தாலும், அதை மாற்றுவதை எளிதாக்குவதால் இந்தக் குறியீட்டைச் சேர்த்துள்ளேன். ஒரு தயாரிப்பு அமைப்பில், சிறந்த செயல்திறனுக்காக `MESSAGE_LENGTH` மாறாது என்று நீங்கள் கருதிக்கொள்ளலாம்.

```rust
    keccak256::keccak256(buffer, HASH_BUFFER_SIZE)
}
```

எத்திரியம் நிலையான `keccak256` செயற்கூற்றைப் பயன்படுத்தவும்.

```rust
fn signatureToAddressAndHash(
        message: str<MESSAGE_LENGTH>, 
        pubKeyX: [u8; 32],
        pubKeyY: [u8; 32],
        signature: [u8; 64]
    ) -> (Field, Field, Field)   // முகவரி, ஹாஷின் முதல் 16 பைட்டுகள், ஹாஷின் கடைசி 16 பைட்டுகள்        
{
```

இந்தச் செயற்கூறு கையொப்பத்தைச் சரிபார்க்கிறது, இதற்குச் செய்தி ஹாஷ் தேவை. பின்னர் அது கையொப்பமிட்ட முகவரியையும் செய்தி ஹாஷையும் நமக்கு வழங்குகிறது. செய்தி ஹாஷ் இரண்டு `Field` மதிப்புகளில் வழங்கப்படுகிறது, ஏனெனில் பைட் வரிசையை விட நிரலின் மற்ற பகுதிகளில் அவற்றைப் பயன்படுத்துவது எளிது.

நாம் இரண்டு `Field` மதிப்புகளைப் பயன்படுத்த வேண்டும், ஏனெனில் புலக் கணக்கீடுகள் ஒரு பெரிய எண்ணின் [மாடுலோ (modulo)](https://en.wikipedia.org/wiki/Modulo) மூலம் செய்யப்படுகின்றன, ஆனால் அந்த எண் பொதுவாக 256 பிட்களுக்கும் குறைவாகவே இருக்கும் (இல்லையெனில் EVM இல் அந்தக் கணக்கீடுகளைச் செய்வது கடினமாக இருக்கும்).

```rust
    let hash = hashMessage(message);

    let mut (hash1, hash2) = (0,0);

    for i in 0..16 {
        hash1 = hash1*256 + hash[31-i].into();
        hash2 = hash2*256 + hash[15-i].into();
    }
```

`hash1` மற்றும் `hash2` ஆகியவற்றை மாற்றக்கூடிய மாறிகளாகக் குறிப்பிட்டு, ஹாஷை அவற்றில் பைட் பைட்டாக எழுதவும்.

```rust
    (
        ecrecover::ecrecover(pubKeyX, pubKeyY, signature, hash), 
```
    
இது [Solidity இன் `ecrecover`](https://docs.soliditylang.org/en/v0.8.30/cheatsheet.html#mathematical-and-cryptographic-functions) ஐப் போன்றது, இதில் இரண்டு முக்கியமான வேறுபாடுகள் உள்ளன:

- கையொப்பம் செல்லுபடியாகவில்லை என்றால், அழைப்பு `assert` இல் தோல்வியடையும் மற்றும் நிரல் கைவிடப்படும்.
- கையொப்பம் மற்றும் ஹாஷிலிருந்து பொது விசையை மீட்டெடுக்க முடியும் என்றாலும், இது வெளிப்புறமாகச் செய்யக்கூடிய செயலாக்கமாகும், எனவே, பூஜ்ஜிய-அறிவுச் சான்றுக்குள் இதைச் செய்வது மதிப்புக்குரியதல்ல. இங்கே யாராவது நம்மை ஏமாற்ற முயன்றால், கையொப்பச் சரிபார்ப்பு தோல்வியடையும்.

```rust
        hash1,
        hash2
    )
}

fn main(
        accounts: [Account; ACCOUNT_NUMBER],
        message: str<MESSAGE_LENGTH>,
        pubKeyX: [u8; 32],
        pubKeyY: [u8; 32],
        signature: [u8; 64],
    ) -> pub (
        Field,  // பழைய கணக்குகள் வரிசையின் ஹாஷ்
        Field,  // புதிய கணக்குகள் வரிசையின் ஹாஷ்
        Field,  // செய்தி ஹாஷின் முதல் 16 பைட்டுகள்
        Field,  // செய்தி ஹாஷின் கடைசி 16 பைட்டுகள்
    )
```

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

```rust
{
    let mut txn = readTransferTxn(message);
```

`txn` மாற்றக்கூடியதாக இருக்க வேண்டும், ஏனெனில் நாம் செய்தியிலிருந்து அனுப்புநர் முகவரியைப் படிப்பதில்லை, கையொப்பத்திலிருந்து அதைப் படிக்கிறோம். 

```rust
    let (fromAddress, txnHash1, txnHash2) = signatureToAddressAndHash(
        message,
        pubKeyX,
        pubKeyY,
        signature);

    txn.from = fromAddress;

    let newAccounts = apply_transfer_txn(accounts, txn);

    (
        hash_accounts(accounts),
        hash_accounts(newAccounts),
        txnHash1,
        txnHash2
    )
}
```

### கட்டம் 2 - ஒரு சேவையகத்தைச் சேர்த்தல் {#stage-2}

இரண்டாவது கட்டத்தில், உலாவியிலிருந்து பரிமாற்றப் பரிவர்த்தனைகளைப் பெற்றுச் செயல்படுத்தும் ஒரு சேவையகத்தைச் சேர்க்கிறோம்.

இதைச் செயல்பாட்டில் காண:

1. Vite இயங்கிக்கொண்டிருந்தால் அதை நிறுத்தவும்.

2. சேவையகத்தை உள்ளடக்கிய கிளையைப் பதிவிறக்கி, தேவையான அனைத்துத் தொகுதிகளும் உங்களிடம் இருப்பதை உறுதிசெய்யவும்.

   ```sh
   git checkout 02-add-server
   cd client
   npm install
   cd ../server
   npm install
   ```

   Noir குறியீட்டைத் தொகுக்க வேண்டிய அவசியமில்லை, இது கட்டம் 1-க்கு நீங்கள் பயன்படுத்திய அதே குறியீடுதான்.

3. சேவையகத்தைத் தொடங்கவும்.

   ```sh
   npm run start
   ```

4. ஒரு தனி கட்டளை வரி சாளரத்தில், உலாவி குறியீட்டை வழங்க Vite ஐ இயக்கவும்.

   ```sh
   cd client
   npm run dev
   ```

5. [http://localhost:5173](http://localhost:5173) இல் உள்ள கிளையன்ட் குறியீட்டிற்கு உலாவவும்

6. நீங்கள் ஒரு பரிவர்த்தனையை வழங்குவதற்கு முன், நான்ஸ் மற்றும் நீங்கள் அனுப்பக்கூடிய தொகையை நீங்கள் தெரிந்து கொள்ள வேண்டும். இந்தத் தகவலைப் பெற, **Update account data** என்பதைக் கிளிக் செய்து செய்தியில் கையொப்பமிடவும்.

   இங்கே நமக்கு ஒரு குழப்பம் உள்ளது. ஒருபுறம், மீண்டும் பயன்படுத்தக்கூடிய ஒரு செய்தியில் கையொப்பமிட நாங்கள் விரும்பவில்லை (ஒரு [மறுஇயக்கத் தாக்குதல் (replay attack)](https://en.wikipedia.org/wiki/Replay_attack)), அதனால்தான் முதலில் எங்களுக்கு ஒரு நான்ஸ் தேவை. இருப்பினும், எங்களிடம் இன்னும் நான்ஸ் இல்லை. தற்போதைய நேரம் போன்ற, ஒரு முறை மட்டுமே பயன்படுத்தக்கூடிய மற்றும் இரு தரப்பிலும் ஏற்கனவே உள்ள ஒரு நான்ஸைத் தேர்ந்தெடுப்பதே இதற்கான தீர்வாகும்.

   இந்தத் தீர்வில் உள்ள சிக்கல் என்னவென்றால், நேரம் சரியாக ஒத்திசைக்கப்படாமல் இருக்கலாம். எனவே அதற்குப் பதிலாக, ஒவ்வொரு நிமிடமும் மாறும் ஒரு மதிப்பில் கையொப்பமிடுகிறோம். இதன் பொருள் மறுஇயக்கத் தாக்குதல்களுக்கான நமது பாதிப்பு சாளரம் அதிகபட்சம் ஒரு நிமிடம் ஆகும். தயாரிப்பில் கையொப்பமிடப்பட்ட கோரிக்கை TLS ஆல் பாதுகாக்கப்படும் என்பதையும், சுரங்கப்பாதையின் மறுபக்கம்---சேவையகம்---ஏற்கனவே இருப்பு மற்றும் நான்ஸை வெளிப்படுத்த முடியும் (அது வேலை செய்ய அவற்றை அறிந்திருக்க வேண்டும்) என்பதையும் கருத்தில் கொண்டால், இது ஏற்றுக்கொள்ளக்கூடிய ஆபத்துதான்.

7. உலாவி இருப்பு மற்றும் நான்ஸைத் திரும்பப் பெற்றவுடன், அது பரிமாற்றப் படிவத்தைக் காட்டுகிறது. இலக்கு முகவரி மற்றும் தொகையைத் தேர்ந்தெடுத்து **Transfer** என்பதைக் கிளிக் செய்யவும். இந்தக் கோரிக்கையில் கையொப்பமிடவும்.

8. பரிமாற்றத்தைப் பார்க்க, **Update account data** என்பதைக் கிளிக் செய்யவும் அல்லது நீங்கள் சேவையகத்தை இயக்கும் சாளரத்தைப் பார்க்கவும். நிலை மாறும் ஒவ்வொரு முறையும் சேவையகம் அதைப் பதிவு செய்கிறது.

        ```
ori@CryptoDocGuy:~/x/250911-zk-bank/server$ npm run start

    > server@1.0.0 start
    > node --experimental-json-modules index.mjs

    Listening on port 3000
    Txn send 0x90F79bf6EB2c4f870365E785982E1f101E93b906 36000 finney (milliEth) 0 processed
    New state:
    0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 has 64000 (1)
    0x70997970C51812dc3A010C7d01b50e0d17dc79C8 has 100000 (0)
    0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC has 100000 (0)
    0x90F79bf6EB2c4f870365E785982E1f101E93b906 has 136000 (0)
    0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 has 100000 (0)
    Txn send 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 7200 finney (milliEth) 1 processed
    New state:
    0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 has 56800 (2)
    0x70997970C51812dc3A010C7d01b50e0d17dc79C8 has 107200 (0)
    0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC has 100000 (0)
    0x90F79bf6EB2c4f870365E785982E1f101E93b906 has 136000 (0)
    0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 has 100000 (0)
    Txn send 0x90F79bf6EB2c4f870365E785982E1f101E93b906 3000 finney (milliEth) 2 processed
    New state:
    0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 has 53800 (3)
    0x70997970C51812dc3A010C7d01b50e0d17dc79C8 has 107200 (0)
    0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC has 100000 (0)
    0x90F79bf6EB2c4f870365E785982E1f101E93b906 has 139000 (0)
    0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 has 100000 (0)
        ```

#### `server/index.mjs` {#server-index-mjs-1}

[இந்தக் கோப்பு](https://github.com/qbzzt/250911-zk-bank/blob/02-add-server/server/index.mjs) சேவையகச் செயல்முறையைக் கொண்டுள்ளது, மேலும் [`main.nr`](https://github.com/qbzzt/250911-zk-bank/blob/02-add-server/server/noir/src/main.nr) இல் உள்ள Noir குறியீட்டுடன் தொடர்பு கொள்கிறது. சுவாரஸ்யமான பகுதிகளின் விளக்கம் இங்கே.

```js
import { Noir } from '@noir-lang/noir_js'
```

[noir.js](https://www.npmjs.com/package/@noir-lang/noir_js) நிரலகம் JavaScript குறியீட்டிற்கும் Noir குறியீட்டிற்கும் இடையே இடைமுகமாகச் செயல்படுகிறது.

```js
const circuit = JSON.parse(await fs.readFile("./noir/target/zkBank.json"))
const noir = new Noir(circuit)
```

எண்கணிதச் சுற்றை ஏற்றவும்---முந்தைய கட்டத்தில் நாம் உருவாக்கிய தொகுக்கப்பட்ட Noir நிரல்---மற்றும் அதைச் செயல்படுத்தத் தயாராகுங்கள்.

```js
// கையொப்பமிடப்பட்ட கோரிக்கைக்கு பதிலளிக்கும் விதமாக மட்டுமே நாங்கள் கணக்குத் தகவலை வழங்குகிறோம்
const accountInformation = async signature => {
    const fromAddress = await recoverAddress({
        hash: hashMessage("Get account data " + Math.floor((new Date().getTime())/60000)),
        signature
    })
```

கணக்குத் தகவலை வழங்க, நமக்குக் கையொப்பம் மட்டுமே தேவை. இதற்குக் காரணம், செய்தி என்னவாக இருக்கும் என்பது நமக்கு ஏற்கனவே தெரியும், எனவே செய்தி ஹாஷும் தெரியும்.

```js
const processMessage = async (message, signature) => {
```

ஒரு செய்தியைச் செயலாக்கி, அது குறியாக்கம் செய்யும் பரிவர்த்தனையைச் செயல்படுத்தவும்.

```js
    // பொது விசையைப் பெறுக
    const pubKey = await recoverPublicKey({
        hash,
        signature
    })
```

இப்போது நாம் சேவையகத்தில் JavaScript ஐ இயக்குவதால், கிளையன்டிற்குப் பதிலாக அங்கேயே பொது விசையை மீட்டெடுக்கலாம்.

```js
    let noirResult
    try {
        noirResult = await noir.execute({
            message,
            signature: signature.slice(2,-2).match(/.{2}/g).map(x => `0x${x}`),
            pubKeyX,
            pubKeyY,
            accounts: Accounts
        })
```

`noir.execute` Noir நிரலை இயக்குகிறது. அளவுருக்கள் [`Prover.toml`](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/Prover.toml) இல் வழங்கப்பட்டவற்றுக்குச் சமமானவை. Viem செய்வதைப் போல, நீண்ட மதிப்புகள் ஒற்றை ஹெக்ஸாடெசிமல் மதிப்பாக (`0x60A7`) அல்லாமல், ஹெக்ஸாடெசிமல் சரங்களின் வரிசையாக (`["0x60", "0xA7"]`) வழங்கப்படுகின்றன என்பதை நினைவில் கொள்ளவும்.

```js
    } catch (err) {
        console.log(`Noir error: ${err}`)
        throw Error("Invalid transaction, not processed")
    }
```

பிழை இருந்தால், அதைப் பிடித்து, பின்னர் எளிமைப்படுத்தப்பட்ட பதிப்பைக் கிளையன்டிற்கு அனுப்பவும்.

```js
    Accounts[fromAccountNumber].nonce++
    Accounts[fromAccountNumber].balance -= amount
    Accounts[toAccountNumber].balance += amount
```

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

```js
let Accounts = [
    {
        address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
        balance: 5000,
        nonce: 0,
    },
```

ஆரம்ப `Accounts` கட்டமைப்பு.

### கட்டம் 3 - எத்திரியம் திறன் ஒப்பந்தங்கள் {#stage-3}

1. சேவையகம் மற்றும் கிளையன்ட் செயல்முறைகளை நிறுத்தவும்.

2. திறன் ஒப்பந்தங்களைக் கொண்ட கிளையைப் பதிவிறக்கி, தேவையான அனைத்துத் தொகுதிகளும் உங்களிடம் இருப்பதை உறுதிசெய்யவும்.

   ```sh
   git checkout 03-smart-contracts
   cd client
   npm install
   cd ../server
   npm install
   ```

3. ஒரு தனி கட்டளை வரி சாளரத்தில் `anvil` ஐ இயக்கவும்.

4. சரிபார்ப்பு விசை மற்றும் Solidity சரிபார்ப்பியை (verifier) உருவாக்கவும், பின்னர் சரிபார்ப்பிக் குறியீட்டை Solidity திட்டத்திற்கு நகலெடுக்கவும்.

   ```sh
   cd noir
   bb write_vk -b ./target/zkBank.json -o ./target --oracle_hash keccak
   bb write_solidity_verifier -k ./target/vk -o ./target/Verifier.sol
   cp target/Verifier.sol ../../smart-contracts/src
   ```

5. திறன் ஒப்பந்தங்களுக்குச் சென்று, `anvil` தொகுதிச்சங்கிலியைப் பயன்படுத்தச் சூழல் மாறிகளை அமைக்கவும்.

   ```sh
   cd ../../smart-contracts
   export ETH_RPC_URL=http://localhost:8545
   ETH_PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
   ```

6. `Verifier.sol` ஐ நிலைநிறுத்தி, முகவரியை ஒரு சூழல் மாறியில் சேமிக்கவும்.

   ```sh
   VERIFIER_ADDRESS=`forge create src/Verifier.sol:HonkVerifier --private-key $ETH_PRIVATE_KEY --optimize --broadcast | awk '/Deployed to:/ {print $3}'`
   echo $VERIFIER_ADDRESS
   ```

7. `ZkBank` ஒப்பந்தத்தை நிலைநிறுத்தவும்.

   ```sh
   ZKBANK_ADDRESS=`forge create ZkBank --private-key $ETH_PRIVATE_KEY --broadcast --constructor-args $VERIFIER_ADDRESS 0x199aa62af8c1d562a6ec96e66347bf3240ab2afb5d022c895e6bf6a5e617167b | awk '/Deployed to:/ {print $3}'`
   echo $ZKBANK_ADDRESS
   ```

   `0x199..67b` மதிப்பு என்பது `Accounts` இன் ஆரம்ப நிலையின் Pederson ஹாஷ் ஆகும். `server/index.mjs` இல் இந்த ஆரம்ப நிலையை நீங்கள் மாற்றியமைத்தால், பூஜ்ஜிய-அறிவுச் சான்றால் புகாரளிக்கப்பட்ட ஆரம்ப ஹாஷைப் பார்க்க நீங்கள் ஒரு பரிவர்த்தனையை இயக்கலாம்.

8. சேவையகத்தை இயக்கவும்.

   ```sh
   cd ../server
   npm run start
   ```

9. வேறு கட்டளை வரி சாளரத்தில் கிளையன்ட்டை இயக்கவும்.

   ```sh
   cd client
   npm run dev
   ```

10. சில பரிவர்த்தனைகளை இயக்கவும்.

11. சங்கிலிசார் நிலை மாறியுள்ளதா என்பதைச் சரிபார்க்க, சேவையகச் செயல்முறையை மறுதொடக்கம் செய்யவும். பரிவர்த்தனைகளில் உள்ள அசல் ஹாஷ் மதிப்பு சங்கிலிசார் சேமிக்கப்பட்ட ஹாஷ் மதிப்பிலிருந்து வேறுபடுவதால், `ZkBank` இனி பரிவர்த்தனைகளை ஏற்காது என்பதைப் பார்க்கவும்.

    இது எதிர்பார்க்கப்படும் பிழையின் வகையாகும்.

        ```
ori@CryptoDocGuy:~/x/250911-zk-bank/server$ npm run start

    > server@1.0.0 start
    > node --experimental-json-modules index.mjs

    Listening on port 3000
    Verification error: ContractFunctionExecutionError: The contract function "processTransaction" reverted with the following reason:
    Wrong old state hash

    Contract Call:
        address:   0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
        function:  processTransaction(bytes _proof, bytes32[] _publicInputs)
        args:                        (0x0000000000000000000000000000000000000000000000042ab5d6d1986846cf00000000000000000000000000000000000000000000000b75c020998797da7800000000000000000000000000000000000000000000000
        ```

#### `server/index.mjs` {#server-index-mjs-2}

இந்தக் கோப்பில் உள்ள மாற்றங்கள் பெரும்பாலும் உண்மையான சான்றை உருவாக்குவது மற்றும் அதைச் சங்கிலிசார் சமர்ப்பிப்பது ஆகியவற்றுடன் தொடர்புடையவை.

```js
import { exec } from 'child_process'
import util from 'util'

const execPromise = util.promisify(exec)
```

சங்கிலிசார் அனுப்ப உண்மையான சான்றை உருவாக்க நாம் [Barretenberg தொகுப்பைப்](https://github.com/AztecProtocol/aztec-packages/tree/next/barretenberg) பயன்படுத்த வேண்டும். கட்டளை வரி இடைமுகத்தை (`bb`) இயக்குவதன் மூலமாகவோ அல்லது [JavaScript நிரலகமான `bb.js`](https://www.npmjs.com/package/@aztec/bb.js) ஐப் பயன்படுத்துவதன் மூலமாகவோ இந்தத் தொகுப்பைப் பயன்படுத்தலாம். குறியீட்டை இயல்பாக இயக்குவதை விட JavaScript நிரலகம் மிகவும் மெதுவாக இருக்கும், எனவே கட்டளை வரியைப் பயன்படுத்த இங்கே [`exec`](https://nodejs.org/api/child_process.html#child_processexeccommand-options-callback) ஐப் பயன்படுத்துகிறோம்.

நீங்கள் `bb.js` ஐப் பயன்படுத்த முடிவு செய்தால், நீங்கள் பயன்படுத்தும் Noir பதிப்போடு இணக்கமான பதிப்பைப் பயன்படுத்த வேண்டும் என்பதை நினைவில் கொள்ளவும். இதை எழுதும் நேரத்தில், தற்போதைய Noir பதிப்பு (1.0.0-beta.11) `bb.js` பதிப்பு 0.87 ஐப் பயன்படுத்துகிறது.

```js
const zkBankAddress = process.env.ZKBANK_ADDRESS || "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512"
```

சுத்தமான `anvil` உடன் தொடங்கி மேலே உள்ள வழிமுறைகளைப் பின்பற்றும்போது நீங்கள் பெறும் முகவரி இதுவாகும்.

```js
const walletClient = createWalletClient({ 
    chain: anvil, 
    transport: http(), 
    account: privateKeyToAccount("0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6")
})
```

இந்தத் தனிப்பட்ட திறவுகோல் `anvil` இல் உள்ள இயல்புநிலை முன்-நிதியளிக்கப்பட்ட கணக்குகளில் ஒன்றாகும். 

```js
const generateProof = async (witness, fileID) => {
```

`bb` இயங்கக்கூடிய கோப்பைப் பயன்படுத்தி ஒரு சான்றை உருவாக்கவும்.

```js 
    const fname = `witness-${fileID}.gz`    
    await fs.writeFile(fname, witness)
```

சான்றை ஒரு கோப்பில் எழுதவும்.

```js
    await execPromise(`bb prove -b ./noir/target/zkBank.json -w ${fname} -o ${fileID} --oracle_hash keccak --output_format fields`)
```

உண்மையில் சான்றை உருவாக்கவும். இந்தப் படி பொது மாறிகளுடன் ஒரு கோப்பையும் உருவாக்குகிறது, ஆனால் அது நமக்குத் தேவையில்லை. அந்த மாறிகளை நாம் ஏற்கனவே `noir.execute` இலிருந்து பெற்றுவிட்டோம்.

```js
    const proof = "0x" + JSON.parse(await fs.readFile(`./${fileID}/proof_fields.json`)).reduce((a,b) => a+b, "").replace(/0x/g, "")
```

சான்று என்பது `Field` மதிப்புகளின் JSON வரிசையாகும், ஒவ்வொன்றும் ஹெக்ஸாடெசிமல் மதிப்பாகக் குறிக்கப்படுகின்றன. இருப்பினும், நாம் அதைப் பரிவர்த்தனையில் ஒற்றை `bytes` மதிப்பாக அனுப்ப வேண்டும், இதை Viem ஒரு பெரிய ஹெக்ஸாடெசிமல் சரத்தால் குறிக்கிறது. இங்கே நாம் அனைத்து மதிப்புகளையும் இணைத்து, அனைத்து `0x` களையும் அகற்றி, பின்னர் முடிவில் ஒன்றைச் சேர்ப்பதன் மூலம் வடிவமைப்பை மாற்றுகிறோம்.

```js
    await execPromise(`rm -r ${fname} ${fileID}`)

    return proof
}
```

சுத்தம் செய்து சான்றை வழங்கவும்.

```js
const processMessage = async (message, signature) => {
    .
    .
    .

    const publicFields = noirResult.returnValue.map(x=>'0x' + x.slice(2).padStart(64, "0"))
```

பொதுப் புலங்கள் 32-பைட் மதிப்புகளின் வரிசையாக இருக்க வேண்டும். இருப்பினும், பரிவர்த்தனை ஹாஷை இரண்டு `Field` மதிப்புகளுக்கு இடையில் பிரிக்க வேண்டியிருந்ததால், அது 16-பைட் மதிப்பாகத் தோன்றுகிறது. இங்கே நாம் பூஜ்ஜியங்களைச் சேர்க்கிறோம், இதனால் அது உண்மையில் 32 பைட்டுகள் என்பதை Viem புரிந்துகொள்ளும்.

```js
    const proof = await generateProof(noirResult.witness, `${fromAddress}-${nonce}`)
```

ஒவ்வொரு முகவரியும் ஒவ்வொரு நான்ஸையும் ஒரு முறை மட்டுமே பயன்படுத்துகிறது, இதனால் சான்று கோப்பு மற்றும் வெளியீட்டு அடைவுக்கான தனிப்பட்ட அடையாளங்காட்டியாக `fromAddress` மற்றும் `nonce` ஆகியவற்றின் கலவையை நாம் பயன்படுத்தலாம்.

```js
    try {
        await zkBank.write.processTransaction([
            proof, publicFields])
    } catch (err) {
        console.log(`Verification error: ${err}`)
        throw Error("Can't verify the transaction onchain")
    }
    .
    .
    .
}
```

பரிவர்த்தனையைச் சங்கிலிக்கு அனுப்பவும்.

#### `smart-contracts/src/ZkBank.sol` {#smart-contracts-src-zkbank-sol}

பரிவர்த்தனையைப் பெறும் சங்கிலிசார் குறியீடு இதுவாகும்.

```solidity
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.21;

import {HonkVerifier} from "./Verifier.sol";

contract ZkBank {
    HonkVerifier immutable myVerifier;
    bytes32 currentStateHash;

    constructor(address _verifierAddress, bytes32 _initialStateHash) {
        currentStateHash = _initialStateHash;
        myVerifier = HonkVerifier(_verifierAddress);
    }
```

சங்கிலிசார் குறியீடு இரண்டு மாறிகளைக் கண்காணிக்க வேண்டும்: சரிபார்ப்பி (`nargo` ஆல் உருவாக்கப்பட்ட ஒரு தனி ஒப்பந்தம்) மற்றும் தற்போதைய நிலை ஹாஷ்.

```solidity
    event TransactionProcessed(
        bytes32 indexed transactionHash,
        bytes32 oldStateHash,
        bytes32 newStateHash
    );
```

நிலை மாறும் ஒவ்வொரு முறையும், நாம் ஒரு `TransactionProcessed` நிகழ்வை வெளியிடுகிறோம்.

```solidity
    function processTransaction(
        bytes calldata _proof,
        bytes32[] calldata _publicFields
    ) public {
```

இந்தச் செயற்கூறு பரிவர்த்தனைகளைச் செயலாக்குகிறது. இது சான்றை (`bytes` ஆக) மற்றும் பொது உள்ளீடுகளை (ஒரு `bytes32` வரிசையாக), சரிபார்ப்பிக்குத் தேவையான வடிவமைப்பில் பெறுகிறது (சங்கிலிசார் செயலாக்கத்தைக் குறைக்கவும், அதன் மூலம் எரிவாயுச் செலவுகளைக் குறைக்கவும்).

```solidity
        require(_publicInputs[0] == currentStateHash,
            "Wrong old state hash");
```

பரிவர்த்தனை நமது தற்போதைய ஹாஷிலிருந்து புதிய ஹாஷுக்கு மாறுகிறது என்பதே பூஜ்ஜிய-அறிவுச் சான்றாக இருக்க வேண்டும்.

```solidity
        myVerifier.verify(_proof, _publicFields);
```

பூஜ்ஜிய-அறிவுச் சான்றைச் சரிபார்க்கச் சரிபார்ப்பி ஒப்பந்தத்தை அழைக்கவும். பூஜ்ஜிய-அறிவுச் சான்று தவறாக இருந்தால் இந்தப் படி பரிவர்த்தனையைத் திரும்பப் பெறுகிறது.

```solidity
        currentStateHash = _publicFields[1];

        emit TransactionProcessed(
            _publicFields[2]<<128 | _publicFields[3],
            _publicFields[0],
            _publicFields[1]
        );
    }
}
```

எல்லாம் சரியாக இருந்தால், நிலை ஹாஷைப் புதிய மதிப்பிற்குப் புதுப்பித்து, ஒரு `TransactionProcessed` நிகழ்வை வெளியிடவும்.

## மையப்படுத்தப்பட்ட கூறின் தவறான பயன்பாடுகள் {#abuses}

தகவல் பாதுகாப்பு மூன்று பண்புகளைக் கொண்டுள்ளது:

- _ரகசியத்தன்மை_, பயனர்கள் படிக்க அங்கீகரிக்கப்படாத தகவல்களைப் படிக்க முடியாது.
- _ஒருமைப்பாடு_, அங்கீகரிக்கப்பட்ட பயனர்களால் அங்கீகரிக்கப்பட்ட முறையில் தவிர தகவல்களை மாற்ற முடியாது.
- _கிடைக்கும் தன்மை_, அங்கீகரிக்கப்பட்ட பயனர்கள் அமைப்பைப் பயன்படுத்தலாம்.

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

[மறைமுக முகவரிகளைப்](https://vitalik.eth.limo/general/2023/01/20/stealth.html) பயன்படுத்தி உண்மையான ரகசிய வங்கியை உருவாக்க முடியும், ஆனால் அது இந்தக் கட்டுரையின் வரம்பிற்கு அப்பாற்பட்டது.

### தவறான தகவல் {#false-info}

[தரவு கோரப்படும்போது](https://github.com/qbzzt/250911-zk-bank/blob/03-smart-contracts/server/index.mjs#L278-L291) தவறான தகவலை வழங்குவது சேவையகம் ஒருமைப்பாட்டை மீறும் ஒரு வழியாகும்.

இதைத் தீர்க்க, கணக்குகளை தனிப்பட்ட உள்ளீடாகவும், தகவல் கோரப்படும் முகவரியை பொது உள்ளீடாகவும் பெறும் இரண்டாவது Noir நிரலை நாம் எழுதலாம். அந்த முகவரியின் இருப்பு மற்றும் நான்ஸ், மற்றும் கணக்குகளின் ஹாஷ் ஆகியவை வெளியீடாக இருக்கும்.

நிச்சயமாக, இந்தச் சான்றை சங்கிலிசார் சரிபார்க்க முடியாது, ஏனெனில் நான்ஸ்கள் மற்றும் இருப்புகளை சங்கிலிசார் இடுகையிட நாங்கள் விரும்பவில்லை. இருப்பினும், உலாவியில் இயங்கும் கிளையன்ட் குறியீட்டின் மூலம் இதைச் சரிபார்க்க முடியும்.

### கட்டாயப்படுத்தப்பட்ட பரிவர்த்தனைகள் {#forced-txns}

L2-களில் கிடைக்கும் தன்மையை உறுதி செய்வதற்கும் தணிக்கையைத் தடுப்பதற்குமான வழக்கமான வழிமுறை [கட்டாயப்படுத்தப்பட்ட பரிவர்த்தனைகள்](https://docs.optimism.io/stack/transactions/forced-transaction) ஆகும். ஆனால் கட்டாயப்படுத்தப்பட்ட பரிவர்த்தனைகள் பூஜ்ஜிய-அறிவுச் சான்றுகளுடன் இணைவதில்லை. பரிவர்த்தனைகளைச் சரிபார்க்கக்கூடிய ஒரே நிறுவனம் சேவையகம் மட்டுமே.

கட்டாயப்படுத்தப்பட்ட பரிவர்த்தனைகளை ஏற்கவும், அவை செயலாக்கப்படும் வரை சேவையகம் நிலையை மாற்றுவதைத் தடுக்கவும் `smart-contracts/src/ZkBank.sol` ஐ நாம் மாற்றியமைக்கலாம். இருப்பினும், இது ஒரு எளிய சேவை-மறுப்புத் தாக்குதலுக்கு நம்மைத் திறக்கிறது. கட்டாயப்படுத்தப்பட்ட பரிவர்த்தனை தவறானதாக இருந்து, அதனால் செயலாக்க முடியாமல் போனால் என்ன செய்வது?

கட்டாயப்படுத்தப்பட்ட பரிவர்த்தனை தவறானது என்பதற்கான பூஜ்ஜிய-அறிவுச் சான்றைக் கொண்டிருப்பதே இதற்கான தீர்வாகும். இது சேவையகத்திற்கு மூன்று விருப்பங்களை அளிக்கிறது:

- கட்டாயப்படுத்தப்பட்ட பரிவர்த்தனையைச் செயலாக்குவது, அது செயலாக்கப்பட்டது என்பதற்கான பூஜ்ஜிய-அறிவுச் சான்று மற்றும் புதிய நிலை ஹாஷ் ஆகியவற்றை வழங்குவது.
- கட்டாயப்படுத்தப்பட்ட பரிவர்த்தனையை நிராகரிப்பது, மற்றும் பரிவர்த்தனை தவறானது (தெரியாத முகவரி, தவறான நான்ஸ் அல்லது போதிய இருப்பு இல்லாமை) என்பதற்கான பூஜ்ஜிய-அறிவுச் சான்றை ஒப்பந்தத்திற்கு வழங்குவது.
- கட்டாயப்படுத்தப்பட்ட பரிவர்த்தனையைப் புறக்கணிப்பது. பரிவர்த்தனையை உண்மையில் செயலாக்க சேவையகத்தைக் கட்டாயப்படுத்த எந்த வழியும் இல்லை, ஆனால் இதன் பொருள் முழு அமைப்பும் கிடைக்கவில்லை என்பதாகும்.

#### கிடைக்கும் தன்மைப் பத்திரங்கள் {#avail-bonds}

நிஜ வாழ்க்கைச் செயலாக்கத்தில், சேவையகத்தை இயங்க வைப்பதற்கு ஏதேனும் ஒரு லாப நோக்கம் இருக்கலாம். ஒரு குறிப்பிட்ட காலத்திற்குள் கட்டாயப்படுத்தப்பட்ட பரிவர்த்தனை செயலாக்கப்படாவிட்டால், எவரும் எரிக்கக்கூடிய கிடைக்கும் தன்மைப் பத்திரத்தை சேவையகம் இடுகையிடுவதன் மூலம் இந்த ஊக்கத்தை நாம் வலுப்படுத்தலாம்.

### மோசமான Noir குறியீடு {#bad-noir-code}

பொதுவாக, திறன் ஒப்பந்தத்தை மக்கள் நம்புவதற்கு, மூலக் குறியீட்டை [தொகுதி ஆராய்வி](https://eth.blockscout.com/address/0x7D16d2c4e96BCFC8f815E15b771aC847EcbDB48b?tab=contract) ஒன்றில் பதிவேற்றுவோம். இருப்பினும், பூஜ்ஜிய-அறிவுச் சான்றுகளின் விஷயத்தில், அது போதாது.

`Verifier.sol` சரிபார்ப்பு விசையைக் கொண்டுள்ளது, இது Noir நிரலின் ஒரு செயற்கூறாகும். இருப்பினும், அந்த விசை Noir நிரல் என்னவாக இருந்தது என்பதை நமக்குச் சொல்லாது. உண்மையில் நம்பகமான தீர்வைக் கொண்டிருக்க, நீங்கள் Noir நிரலை (மற்றும் அதை உருவாக்கிய பதிப்பை) பதிவேற்ற வேண்டும். இல்லையெனில், பூஜ்ஜிய-அறிவுச் சான்றுகள் பின்கதவு கொண்ட வேறுபட்ட நிரலைப் பிரதிபலிக்கக்கூடும்.

தொகுதி ஆராய்விகள் Noir நிரல்களைப் பதிவேற்றவும் சரிபார்க்கவும் நம்மை அனுமதிக்கும் வரை, நீங்களே அதைச் செய்ய வேண்டும் (முன்னுரிமையாக [IPFS](/developers/tutorials/ipfs-decentralized-ui/) க்கு). பின்னர் அதிநவீன பயனர்கள் மூலக் குறியீட்டைப் பதிவிறக்கம் செய்து, அவர்களே அதைத் தொகுத்து, `Verifier.sol` ஐ உருவாக்கி, அது சங்கிலிசார் உள்ளதைப் போலவே இருப்பதைச் சரிபார்க்க முடியும்.

## முடிவுரை {#conclusion}

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

[எனது மேலும் பல பணிகளை இங்கே காணுங்கள்](https://cryptodocguy.pro/).

## நன்றிகள் {#acknowledgements}

- ஜோஷ் க்ரைட்ஸ் இந்தக் கட்டுரையின் வரைவைப் படித்து, ஒரு சிக்கலான Noir சிக்கலில் எனக்கு உதவினார்.

மீதமுள்ள ஏதேனும் பிழைகள் என்னுடைய பொறுப்பாகும்.