برنامه نویسی

ساخت مینا: راهنمای نوشتن قرارداد هوشمند اجرای درختان مرکل با استفاده از 01J

در این مقاله به بررسی درختان مرکل در اکوسیستم مینا می پردازیم و کاربرد عملی آنها را با ایجاد گام به گام یک برنامه ZK رای دهنده نشان می دهد.

مقدمه

یک blockchain از زنجیره ای از بلوک ها تشکیل شده است که هر بلوک حاوی یک یا چند معاملات است. هر بلوک توسط یک هش رمزنگاری که نمایانگر محتوای آن ، از جمله معاملات و ابرداده ها است ، مشخص می شود.

در یک بلوک ، معاملات با استفاده از یک درخت مرکل ، که در آن هر معامله توسط یک هش نشان داده می شود ، ساختار می شوند. این هشدهای معامله به عنوان برگهای درخت عمل می کنند. هر جفت معامله به صورت بازگشتی با هم جمع می شوند تا زمانی که یک هش ریشه ای به نام ریشه مرکل بدست آید. این ریشه Merkle ، به همراه سایر ابرداده های بلوک ، سپس در هدر بلوک قرار می گیرد و از یکپارچگی داده ها و تأیید کارآمد اطمینان می دهد.

شرح تصویر

در مینا ، حساب های ZKAPP فقط می توانند مقدار محدودی از داده ها را برای جلوگیری از نفخ ذخیره کنند. از این رو ، نیاز به یک درخت مرکل ، که امکان مراجعه به داده های خارج از زنجیره را با ذخیره تنها یک زنجیره هش در هر زنجیره ای فراهم می کند ، از این طریق مینا را قادر می سازد تا موجز خود را حفظ کند.

ریشه درخت Merkle خلاصه و تأیید یک مجموعه از داده ها است و برای اثبات وجود برخی از داده ها در درخت مرکل مورد استفاده قرار می گیرد ، ریشه یک نمایش جمع و جور از یک مجموعه بزرگ از داده ها در نظر گرفته می شود ، بنابراین این امکان تأیید و تأیید کارآمد را فراهم می کند یکپارچگی داده ها در داخل سیستم blockchain.

یکی دیگر از مزایای درختان مرکل ، شاهد است که به عنوان یک Merkle Proof یا Merkle Path نیز شناخته می شود. شاهد مسیری از یک گره برگ خاص به بالای درخت است که ریشه است. شاهدان مرکل اثبات گنجاندن هستند که ثابت می کند یک قطعه خاص از داده ها

شهادت

شما می توانید مقادیر زیادی از داده های خارج از زنجیره را ارجاع داده و در گنجاندن قسمت های بسیار خاص آن داده ها با تنها یک هش کوچک – ریشه – و یک شاهد اثبات کنید.

بیایید ساختمان را شروع کنیم.

ما با نصب zkapp-cliبشر

npm install -g zkapp-cli
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

بعد از نصب ما این دستور را برای ایجاد یک پروژه وارد خواهیم کرد

zk project votingNft
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ما این انتخاب را خواهیم داشت تا یکی از گزینه های موجود در تصویر زیر را انتخاب کنیم ، گزینه ها را انتخاب کرده و انتخاب کنیم none

شرح تصویر

پس از انتخاب ، این پروژه ایجاد می شود ، پس از اتمام کار ما از ترمینال با استفاده از ترمینال حرکت خواهیم کرد

cd votingNft
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ما باید برخی از پرونده هایی را که به همراه پروژه تولید شده اند حذف کنیم زیرا ما از آنها استفاده نخواهیم کرد. با این دستور ، پرونده ها را حذف خواهیم کرد

cd src
rm Add.test.ts Add.ts interact.ts
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

سپس در حالی که هنوز در src دایرکتوری ، ما این دستور را اجرا می کنیم تا پرونده های لازم را که از آن خارج می شویم ایجاد کنیم src فهرست راهنما

touch Voting.ts Voting.test.ts

//exit the directory
cd ..
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ما آماده شروع به ساخت و ساز هستیم بنابراین بیایید باز کنیم Voting.ts پرونده ، ما بسته های لازم را که برای گرفتن این توپ نیاز داریم وارد می کنیم

import { Field, SmartContract, state, State, method, Struct, MerkleWitness, Poseidon, Provable } from "o1js";
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ما باید یک ارتفاع درخت را تعریف کنیم ، ارتفاع درخت تصمیم می گیرد که چند لایه از گروه بندی مورد نیاز است ، بر این که داده ها به سرعت و به راحتی قابل تأیید است و ما یک کلاس شاهد مرکل را ایجاد خواهیم کرد

ارتفاع بیان می کند که هنگام مشخص کردن شاهد چه تعداد برگهایی را می توانیم داشته باشیم ، ما هنگام نگاهی به داده های موجود در درخت ، این کار را در عمل مشاهده خواهیم کرد

export const treeHeight = 4

export class MerkleWitness4 extends MerkleWitness(treeHeight){}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

حال بیایید قرارداد اصلی رأی گیری اصلی خود را ایجاد کنیم و آن را آغاز کنیم

export class Voting extends SmartContract{
    // declare state variables
    @state(Field) voters = State()
    @state(Field) winningNft = State()
    @state(Field) winningVotes = State()

    // root of the merkle tree
    @state(Field) treeRoot = State()

    init(){
      super.init()
      this.voters.set(Field(1))
    }

   // initialize the state on the contract
       @method async initState(initialRoot: Field){
     this.treeRoot.set(initialRoot)
   }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ما متغیرهای حالت را برای نگه داشتن تعریف می کنیم votersبا winningNftبا winningVotes و treeRoot، سپس وضعیت قرارداد را آغاز می کنیم.

قبل از ادامه ، باید برای NFT کلاس ایجاد کنیم و روشی برای رأی دادن ایجاد کنیم.

export class NFT extends Struct({nftName: Field, nftCreator: Field, nftVotes: Field}){

//method for the vote
vote(){
  this.nftVotes = this.nftVotes.add(1)
 }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ما با تأیید حضور NFT در درخت مرکل شروع می کنیم. این روند رأی دهی است. سرانجام ، درخت Merkle به روز می شود تا هرگونه تغییر در NFT را منعکس کند. در قرارداد هوشمند رای گیری ، ما روش را ایجاد می کنیم voteForNft

export class Voting extends SmartContract{
  // declare state variables
  @state(Field) voters = State()
  @state(Field) winningNft = State()
  @state(Field) winningVotes = State()

  // root of the merkle tree
  @state(Field) treeRoot = State()
     init(){
       super.init()
       this.voters.set(Field(1))
     }

  // initialize the state of the contract
  @method async initState(initialRoot: Field){
    this.treeRoot.set(initialRoot)
   }

 //method to vote for the nft
 @method async voteForNft(nft: NFT, witness: MerkleWitness4){

  //Get the tree root and see if the nft is in it
  const treeRoot = this.treeRoot.getAndRequireEquals()

  // check to see whether the nft is in the merkle tree, the way we 
  // check to see if the nft is within the tree is by the witness       

  const nftRoot = witness.calculateRoot(Poseidon.hash(NFT.toFields(nft)))

  // check if the nft is in the tree, if it is not in the tree, the transaction will fail and the state will not be updated

   nftRoot.assertEquals(treeRoot)

  //if the nft is in the tree then the nft can be voted on
  nft.vote()

  //update the tree with the new nft hash
  const newNftRoot = witness.calculateRoot(Poseidon.hash(NFT.toFields(nft)))
  this.treeRoot.set(newNftRoot)

//we want to increment the number of voters after a vote has been cast      this.voters.set(this.voters.getAndRequireEquals().add(1))

//we are going to check if the nft has more votes than the current winning nft then we will update the winning nft

  const winningNft = this.winningNft.getAndRequireEquals()
  const winningVotes = this.winningVotes.getAndRequireEquals()

  const newWinningNft = Provable.if(winningVotes.lessThan(nft.nftVotes), nft.nftName, winningNft)
  const newWinningVotes = Provable.if(winningVotes.lessThan(nft.nftVotes), nft.nftName, winningVotes)

this.winningNft.set(newWinningNft)
this.winningVotes.set(newWinningVotes)
  }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

این روش NFT برنده بر روی درخت مرکل را تأیید ، آرا و به روز می کند.

  1. اول ، ما NFT را در درخت با بازیابی ریشه مرکل ، هش کردن با Poseidon و بررسی اینکه آیا در درخت از طریق یک شاهد مرکل وجود دارد ، تأیید می کنیم.

جدیدپوزیدون: Poseidon یک تابع هش برای سیستم های ضد دانش صفر است ، ما از آن استفاده می کنیم زیرا فقط متغیرهای میدانی را ذخیره می کند اما از آنجا که NFT یک است struct ما باید آن را به عنوان زمینه منتقل کنیم

  1. ما درخت رای و به روزرسانی را بازی می کنیم ، اگر معتبر باشد ، با ما تماس می گیریم nft.vote()، و ریشه Merkle را به روز کنید و ریشه جدیدی را در این. Treeroot تنظیم کنید.

  2. ما با افزایش تعداد رای دهندگان را افزایش می دهیم voters برای ردیابی تعداد شرکت کنندگان.

  3. سرانجام ما NFT برنده را به روز می کنیم ، با استفاده از Provable.if برای مقایسه آرا و به روزرسانی winningNft وت winningVotes، اگر NFT جدید رای بیشتری داشته باشد.

با این کار ما قرارداد هوشمند خود را آماده کرده ایم ، اکنون باید قرارداد هوشمند خود را آزمایش کنیم.

بیا بریم

ما به سمت خود حرکت خواهیم کرد Voting.test.ts پرونده و اضافه کردن برخی از کد ها ، اولین کاری که باید انجام دهیم وارد کردن بسته ها و پرونده های لازم است

import { AccountUpdate, CircuitString, Field, MerkleTree, Mina, Poseidon, PrivateKey, PublicKey } from 'o1js';
import { treeHeight, NFT, MerkleWitness4, Voting } from './Voting';

حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

سپس ما برای تست ها تنظیم کردیم

let proofsEnabled = false;

function createNFT(nftName: string, nftCreator: string) {
    const nft = new NFT({
        nftName: Poseidon.hash(CircuitString.toFields(CircuitString.fromString(nftName))),
        nftCreator: Poseidon.hash(CircuitString.toFields(CircuitString.fromString(nftCreator))),
        nftVotes: Field(0)
    });
    return nft;
}

function createTree(): MerkleTree {
    const tree = new MerkleTree(treeHeight);

    const nft0 = createNFT("nft0", "creator0");
    const nft1 = createNFT("nft1", "creator1");
    const nft2 = createNFT("nft2", "creator2");
    const nft3 = createNFT("nft3", "creator3");

    tree.setLeaf(0n, Poseidon.hash(NFT.toFields(nft0)));
    tree.setLeaf(1n, Poseidon.hash(NFT.toFields(nft1)));
    tree.setLeaf(2n, Poseidon.hash(NFT.toFields(nft2)));
    tree.setLeaf(3n, Poseidon.hash(NFT.toFields(nft3)));

    return tree;
}


describe('Voting', () => {
    let deployerAccount: Mina.TestPublicKey,
    deployerKey: PrivateKey,
    senderAccount: Mina.TestPublicKey,
    senderKey: PrivateKey,
    zkAppAddress: PublicKey,
    zkAppPrivateKey: PrivateKey,
    zkApp: Voting,
    tree: MerkleTree;

    beforeAll(async () => {
        if (proofsEnabled) await Voting.compile();
    });

    beforeEach(async () => {
        const Local = await Mina.LocalBlockchain({ proofsEnabled });
        Mina.setActiveInstance(Local);
        [deployerAccount, senderAccount] = Local.testAccounts;
        deployerKey = deployerAccount.key;
        senderKey = senderAccount.key;

        zkAppPrivateKey = PrivateKey.random();
        zkAppAddress = zkAppPrivateKey.toPublicKey();
        zkApp = new Voting(zkAppAddress);
        tree = createTree();
    });

async function localDeploy() {
        const txn = await Mina.transaction(deployerAccount, async () => {
            AccountUpdate.fundNewAccount(deployerAccount);
            await zkApp.deploy();
            await zkApp.initState(tree.getRoot());
        });
        await txn.prove();
        await txn.sign([deployerKey, zkAppPrivateKey]).send();
    }
})
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

یک مورد که ما متوجه می شویم عملکردی برای ایجاد یک درخت با آن است createTree عملکرد ، جایی که ما داده های ساختگی برای NFT هایی داریم که می خواهیم به آنها رأی دهیم.

ما همچنین یک localDeploy تابعی که ما تماس می گیریم

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا