ゼロ知識証明とSymbolブロックチェーンとzkRollupについて

今回はゼロ知識証明について解説します。
zkRollupは少しややこしいので、先に概念実証用のライブラリとして公開されているzkp-fishを利用して説明します。

https://github.com/bordjoski/zkp-fish

:::note alert
このパッケージは非推奨となりました
zkp-fishはコンセプトの証明として開発されたもので、誰のレビューも受けていません。学習曲線の一部であり、実稼働環境での使用は意図していません。
:::

まずは実際に動くプログラムを紹介します。最後にtrueが出れば成功です。

```js
(script = document.createElement('script')).src 
= 'https://xembook.github.io/nem2-browserify/zkp-fish-0.1.2.js';
document.getElementsByTagName('head')[0].appendChild(script);
```


```js
zkp = require('/node_modules/zkp-fish');

//セッティング
agreement = await zkp.Agreement.generateAgreement();
jsonAgreement = JSON.stringify(agreement.toJSON());


//ユーザへの証明リクエスト作成
agreementVerifier = await zkp.Agreement.fromJSON(JSON.parse(jsonAgreement));
verifier = new zkp.Verifier(agreementVerifier);
proofRequest = verifier.getProofRequest();

//ユーザが証明キーを生成
agreementClient = await zkp.Agreement.fromJSON(JSON.parse(jsonAgreement));
client = new zkp.Client(agreementClient);
claim = client.getClaim();
proof = client.getProof(proofRequest, 'password1111');


//サービスプロバイダが検証キーを生成
client2 = new zkp.Client(agreementVerifier);
secret2 = client2.getSecret('password1111');

//サービスプロバイダが検証
success = verifier.verify(proof, claim, secret2);
//> true
```
プログラムの流れを図示するとこんな感じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29187/2ada2423-10e1-55fd-408d-f2d470b9e3ef.png)


上のスクリプト例で説明すると、ゼロ知識証明とは「ユーザが`password1111`を知っていることを、ネットワーク上に直接テキストを乗せずに、サービスプロバイダへ伝える方法」です。

現在、クレジットカードなどは暗号化したものをネットワーク上に載せてやり取りしています。暗号化は陳腐化と進化の歴史であり、古い暗号化されたテキストはいずれ解読されてしまう可能性があります。ブロックチェーンのように永遠に残るデータ上ではメッセージは限りなくランダムに近い状態でやり取りすることが望まれます。ゼロ知識証明とは相手がパスワードを知っているかをyes、またはnoだけ判別できる手法です。

# プログラムの流れ

まず、ゼロ知識証明の手続きを開始する前にセッティングを行っておきます。
json化してユーザとサービスプロバイダに配布しておきましょう。

```js
//セッティング
agreement = await zkp.Agreement.generateAgreement();
jsonAgreement = JSON.stringify(agreement.toJSON());

```

ゼロ知識で証明したいことが発生した場合に、サービスプロバイダは証明リクエストを作成してユーザに渡します。証明リクエストは作成するたびにランダム値となるため、攻撃者は推測することができません。

```js
//ユーザへの証明リクエスト作成

agreementVerifier = await zkp.Agreement.fromJSON(JSON.parse(jsonAgreement));
verifier = new zkp.Verifier(agreementVerifier);
proofRequest = verifier.getProofRequest();
```
出力

> '1077273354040466516634136157452724803750564807733999473855813627820015545761558024217447006732473651969747047504283927597898775825780756285517555042404010252357339813832625587575284219043156837148825098891805392077823848048084068277966876276517763931910672095519167725467734271183982968214668920398405349632661637621701688293723756567756560466121088567798817305242657929545303789672761644506008354853108615081081137466618047299641667543390564783650490279319939482369726921413945749209175236310153492511807179406934904810292465166948055145182380864331496430232977522902111972438233115304688939594153922154840961222536680823398557139808075714910102665862295385605760832121676671833211490759651177809988953080936537558137880509506074756565180422276425755302842080355398470518963967565790841748031810833424698596172144943277007618553096274100832086850928842127030089293279438098187975740162811025381371355887599112869199167912178247434565489646719945212088027647635711609076142603412739654378187071027879034741578302254683157370065130360669808561620715954032155095141011013889859652826334093186981569810955975366654450318651909290809493600921404296742776124058854865295863075345861306485339264685696307791683396371874044085204182133713927361290406078264196667908426504111350035491681345208002777589989500417284946562593089242676307651653339649786375273588638428273296531913440051589097394849384282497626858739581869130721393523438952623607923570525167629589986337655483763769822066070810469357167065270831717766186150652974365910435704636711612682203532995778515847393849520244384809831394306225082054559694410128094958009186604641653321960330325287853730654001577220994896152534188700542845317992228338233172699680565824972543445970562615217572481469536133843206765467711511513697551455893130645831482945560620846079243603996067974150323086737768544069059326393941556017867071790765999489823501329579073299469187386291224845797503276628909617603146156342807572228673528516097259235776320099040557761345545510194051887884949900088136756632352412937404798877589618123113030060622924363012624650727415683943324446333750639586984255983208203678898077514866586797291421869927937028879431173644009428155039347939986120780449325939149671660262584976098965696204690373414067759931052915438070837802613790489097769774564361871729155647924802493935965184497633960179056845047327504213105112838157833330678305792071121304599960688072183259107509920327600130631727314222945648112879'


証明リクエストを受け取ったユーザは事前に行ったagreementと証明リクエストを使って知っていることを証明したい証明キーを作成します。

```js
//ユーザが証明キーを生成

agreementClient = await zkp.Agreement.fromJSON(JSON.parse(jsonAgreement));
client = new zkp.Client(agreementClient);
claim = client.getClaim();
proof = client.getProof(proofRequest, 'password1111');
```

出力
> '-2075940721516164457698078501195287837959796347783945178022867272566194652979252145800476574921920285437727898547726084768542763615715504371305458893369669238634861319766828177756016071260831869307794426312121051759047526946589661241224958156447084603954785287198260525436925020131412595319153998256514886299539749196081020972862385247523998429512028662214005766870353060480749774578732644844221995132703530433190874721239206472473252203648572870269216099587699973650499036596557580497597897842294586664573422798654702252851948800855497234504151579645891758958559028149676840647276456086392726713158999091922745136115695597258456520821987759092908671297470895220355746157749549037633494040343898561284535559301703598195441034691580814833098855632182290595354195648610901986876527548681108122242314879814288870151116341625271546886602415370121345266359091489844058235473217075990400542190910929822246756361884175656421286829776512592749950442023870826942896260808098974665343044488032388411664483021477132518103190249709834611516579479204477030486468008776165773786833704877559813121352366348243808946181958248415851172225011956460162577958595907436055136055771235786240970140596222061996840041068070302049522173565501267891448037732622982110000425251003888951329896313146901338807388921162962489710852559331137323732289704580621404588980437472454215011747898046742772048056428217283249522824233060441225138101797697678332941746384895094597043737696676672324208210053875407222462926200777470497760860781515214553232331748228573062535961575988665237859590773928969992179814752553904620422756079241692688744406356286371164583248767328875972312156495134786809446683634914168076939104216848818628876233381321847755069123094524211170115829227063341866341323001579383895929236074677287794011113154477668918202229059723761327304490037782794313488398672852184285269805368085490247940652058332899732313763163092575013393622631959770304020736506858638457770000718202241962766468753114686368288866133741455015510222370628990539263720165539663511932893692107525327796961161489645338148321795364450646290928090823704918929623483790198759706731310763861975759239997412023080202267289880964763709176070177404240066695585885490602356728865436300798667288451185105930371991451392819558904186646968244817141430439132857905564096855246130101617760998190483335599471141522762475308079475255176652499297442422405303291240094358204005002293441386086575534891427966233441435980330360714314258463389505'



サービスプロバイダ側は、ユーザ側が知っていると主張するパスワードとagreementを使って検証キーを作成しましょう。


```js

//サービスプロバイダが検証キーを生成
client2 = new zkp.Client(agreement);
secret2 = client2.getSecret('password1111');
```

出力
>'8564028355943206187984835458291112258851471438979258846435169103095240436198389437951049260954475432087183868588000114859744781213059577046743234629028315398609818306393495570527463799588918945846576324374124118697552884625235883104321844141984673646316693744620111332796286336777501851645533546013884514953'




ユーザの証明キー、請求キー、サービスプロバイダの検証キーを使って検証します。

```js
//サービスプロバイダが検証
success = verifier.verify(proof, claim, secret2);
```

# Symbolブロックチェーンに活用してみましょう。

べつにブロックチェーンを使う必要は無いのですが、
転送トランザクションのメッセージに証明キーを登録することで、
限りなくランダムに近い、サービスプロバイダにしか意味が解らないメッセージで
アカウントがとある情報を知っていることを証明できます。
検証キーが漏洩しない限り、未来永劫そのメッセージが解読されることはありません。

用途としては、パスワードやカード番号、DIDに関わる情報などに利用できそうです。
今のところSymbolブロックチェーンで活用できるのはここまでです。

では次のステップは？
証明キーをブロックチェーンに登録し、ブロックチェーンによる検証が成功することで新たなトランザクションの発生やワールドステートの状態を変化させられるようになれば、新たな展開が見えてくるかもしれません。

そして、今もっとも注目されているのがzkRollupです。

# zkRollupは？

zkRollupについては、ものすごく分かりやすい記事があるのでこちらで完全に理解しましょう。

https://zenn.dev/serinuntius/articles/f56b3dc2871a03

先ほど提示したサンプルプログラムと違い、
トランザクションも全て公開していて何一つ秘密にしていることは無いように見えます。
実はこれ、膨大な計算しないと導き出せない証拠を、計算せずに（簡単な検算で）確認する方法のようです。こういうこともゼロ知識証明と言うのですね。

アンカリングとの違いを考察すると、メインチェーンでの検算結果が否であればサブチェーンは次のブロックに進めないということになりそうな点でしょうか。とりあえず進めるのが楽観的ロールアップ（ただし嘘ついてたら罰金）という認識です。

さて、SymbolブロックチェーンとNEM(NIS)のzkRollup構想についてですが、どういった進捗なのかコアチームからのメッセージを翻訳しておきます。

>  今はまだ、ロールアップの実装が完全に設計されているわけではなく、研究段階のものです。どの回路を使うか、開発・最適化もまだ行っていません。
暗号技術者と議論する上で興味深いのは、バリデータのインセンティブをどのように扱うか、また分散型ファーストのモデルでこれを構築しようとしていることです。これに対して、他のL2実装（Starkware、zk-sync、Aleo、Polygon）は、厳格で中央集権的なバリデータセットに問題があるようです。

今、非常に注目されている技術ではありますが、バリデータの不正に対して分散的に防ぐモデルを構築できていない研究段階のものが多いようです。もし、ご興味のある方がいらっしゃいましたらぜひDiscordまでご参加ください。

また、イーサリアムやポルカドットがメインチェーンをシンプルにしている一方で、Symbolはメインチェーンに充実した機能をそろえています。Symbolのスマコンがチューリング完全性を放棄しているため、昨今のチェーンに発生している急激なリソースの枯渇化がSymbolにも同様に訪れるかどうかは分かりませんが、今後利用が増える中でSymbolのリソースが枯渇するようなことがあれば、サブチェーンとの役割分担などの議論が発生するもしれません。