برنامه نویسی

Niftyzk tutorials 3 – Edwards-curve Digital Signatures

Niftyzk از EdDSA که یک طرح امضای دیجیتال رمزنگاری عمومی است پشتیبانی می کند. این آموزش حاوی اطلاعاتی در مورد کد تولید شده است و برای درک همه چیز باید آموزش های قبلی را بخوانید.

بیایید با شروع کنیم niftyzk init

Setting up your current directory
? What project do you want to scaffold? EdDSA signature verification
? Choose the hashing algorithm to use:  mimc7
? Do you wish to add public inputs to sign? (E.g: address,amount) yes
? Enter the name of the public inputs in a comma separated list (no numbers or special characters):  address,amount
Generating circuits
Generating javascript
Done
وارد حالت تمام صفحه شوید

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

بنابراین ما EdDSA را با mimc7 انتخاب کردیم و تعدادی ورودی به علامت، آدرس، مقدار اضافه کردیم

استفاده از هش MiMC7 به این معنی است که باید از کلیدی برای ایجاد هش استفاده کنیم که مخفی نگه داشته می شود. اما برای امضای EdDSA با استفاده از MiMC، از کلید استفاده نمی شود. این تصمیم برای عدم استفاده از کلید برای امضا توسط توسعه دهندگان circomlib گرفته شد، زیرا استفاده از کلید پیش فرض مشکلی نیست، داده های امضا شده یک هش عمومی است.

بیایید مدارهای تولید شده را ببینیم، می بینید که یک وجود دارد circuit.circom فایل

pragma circom 2.0.0;

include "../node_modules/circomlib/circuits/eddsamimc.circom";
include "../node_modules/circomlib/circuits/mimc.circom";

template VerifySignature(){
    signal input message;
    signal input address;
    signal input amount;

    signal input k;

    signal input Ax;
    signal input Ay;
    signal input S;
    signal input R8x;
    signal input R8y;

    component eddsa = EdDSAMiMCVerifier();

    component mimc7Hash = MultiMiMC7(3, 91);
    mimc7Hash.k <== k;
    mimc7Hash.in[0] <== message;
    mimc7Hash.in[1] <== address;
    mimc7Hash.in[2] <== amount;


    eddsa.enabled <== 1;
    eddsa.Ax <== Ax;
    eddsa.Ay <== Ay;
    eddsa.S <== S;
    eddsa.R8x <== R8x;
    eddsa.R8y <== R8y;
    eddsa.M <== mimc7Hash.out;

    }

component main {public [message,address,amount]}  = VerifySignature(); 

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

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

بنابراین وابستگی های مورد نیاز را وارد می کنیم، از الگوریتم هش یکسانی برای امضاها و هش پیام استفاده می کنیم.

بنابراین ما یک message ورودی، address و amount که اضافی بودند و این ورودی‌های عمومی ما هستند که برای تأیید در زنجیره آشکار می‌شوند.

k راز مورد استفاده برای هش کردن mimc است

Ax،Ay نقاطی هستند که برای کلید عمومی استفاده می شوند، S، R8x،R8y پارامترهای امضا هستند که توسط تابع امضا برگردانده می شوند.

ما یک هش MiMC7 را با استفاده از آن محاسبه می کنیم k و سپس فقط ورودی ها را به eddsa سیگنال های ورودی الگو با مشخص کردن eddsa.enabled <== 1; ما ادعا می کنیم که امضا باید برای هش معتبر باشد. حال اجازه دهید به برخی از جاوا اسکریپت نگاه کنیم
حساب ها با استفاده از EdDSA ایجاد می شوند:

/**
 * The signature parameters used for verifying pedersen hash signed EdDSA
 * @typedef {Object} Account
 * @property {Buffer} prvKey - Private key buffer
 * @property {Uint8Array[]} pubKey - Public key is a tuple of 32 bit Uint8Array
 */

/**
 * @param {any} eddsa - The EdDSA object
 * @returns {Account}
 * Generate a new account which composes of a private and public key
*/
export function generateAccount(eddsa) {
    const prvKey = rbytes();
    const pubKey = eddsa.prv2pub(prvKey);
    return {
        prvKey,
        pubKey
    }
}
وارد حالت تمام صفحه شوید

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

ما از 32 بایت برای حساب کاربری استفاده می کنیم rbytes() یک بافر تصادفی برمی گرداند. استفاده می کند crypto.randomBytes(32)کلیدهای عمومی یک سری Uint8Array هستند که هر کدام 32 بیت اندازه دارند.

/**
 * 
 * @param {Array} pubKey - Used for computing an address
 * @param {bigin | number} key -The key used for the mimc7 hash
 * @returns 
 */

export async function getAddressFromPubkey(pubKey, key) {
    return mimc7(pubKey, key);
}

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

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

ما “آدرس ها” را از کلید عمومی با هش کردن آن محاسبه می کنیم. این آدرس‌ها آدرس‌های بلاک چین معتبر نیستند اما برای انتزاع حساب قابل استفاده هستند. شما می توانید آدرس ها را از یک کلید عمومی با استفاده از طرح ها و مسیرهای مشتق مختلف استخراج کنید. این به شما بستگی دارد که چه چیزی را انتخاب می کنید.

/**
 * @typedef {Object} Message
 * @property {string | bigint} message
 * @property {string | bigint} address
 * @property {string | bigint} amount
 */

/**
 * 
 * @param {Message} data - The data content of the message. The hash of the data object will be signed
 * @param key {number | bigint} - The key used for the mimc7 hash
 * @returns {bigint} Returns a mimc7 hash
 */
export async function computeMessageHash(data, key) {
    return await mimc7(
        [
            BigInt(data.message),
            BigInt(data.address),
            BigInt(data.amount)
        ], key)
}

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

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

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

/**
 * @param {any} eddsa - the built EDDSA
 * @param {bigint} messagehash - The poseidon hash of the message
 * @param {Buffer} prvKey - The private key used to sign the message 
 * @returns {Signature} signature
 */
export function signMessage(eddsa, messageHash, prvKey) {
    const signature = eddsa.signMiMC(prvKey, eddsa.F.e(messageHash));
    const pubKey = eddsa.prv2pub(prvKey);
    assert(eddsa.verifyMiMC(eddsa.F.e(messageHash), signature, pubKey))

    return {
        signature,
        pubKey
    }
}

/**
 * @typedef {Object} Signature
 * @property {Uint8Array[]} R8
 * @property {bigint} S
 * /

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

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

امضای پیام از تابع فوق استفاده می کند. شما یک شی eddsa، هش پیام محاسبه شده و کلید خصوصی ایجاد شده برای حساب شما را ارسال می کنید. می توانید نوع امضای تعریف شده با JsDoc را ببینید.

/**
 * @typedef {Object} SignatureParameters
 * @property {bigint} Ax
 * @property {bigint} Ay
 * @property {bigint} R8x
 * @property {bigint} R8y
 * @property {bigint} S
 */

/**
 * @param {any} eddsa
 * @param {Uint8Array[]} pubKey - The public key of the account
 * @param {Signature} signature - The signature of the signed message
 * @returns {SignatureParameters} - The signature parameters are prepared parameters, ready to use for the circuit
 */
export function getSignatureParameters(eddsa, pubKey, signature) {
    return {
        Ax: eddsa.F.toObject(pubKey[0]),
        Ay: eddsa.F.toObject(pubKey[1]),
        R8x: eddsa.F.toObject(signature.R8[0]),
        R8y: eddsa.F.toObject(signature.R8[1]),
        S: signature.S
    }
}

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

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

حال برای استفاده از این امضا در circom، باید آن را تبدیل کنیم. بنابراین پارامترهای امضا را برای تأیید با تابع بالا دریافت می کنیم. /test/input.js

/**
 * This is a test input, generated for the starting circuit.
 * If you update the inputs, you need to update this function to match it.
 */
export async function getInput(){

    await buildHashImplementation()
    const eddsa = await getEDDSA();

    const account = generateAccount(eddsa);
    const key = 313; // just an example key for tests
    const message = rbigint();
    const address = rbigint()
    const amount = rbigint()
    const messageHash = await computeMessageHash({message,address,amount}, key)

    const signedMessage = signMessage(eddsa, messageHash, account.prvKey);

    const signatureParameters = getSignatureParameters(eddsa, account.pubKey, signedMessage.signature)

    return {
        Ax: signatureParameters.Ax, 
        Ay: signatureParameters.Ay,
        S: signatureParameters.S,
        R8x: signatureParameters.R8x,
        R8y: signatureParameters.R8y,

        k: key,
        address,
        amount,
        message
    }
}
وارد حالت تمام صفحه شوید

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

و در بالا می توانید ببینید که چگونه ورودی مدارها را محاسبه می کنیم. برای بارگذاری مجدد داغ و تست ها استفاده می شود

اکنون به شما نشان خواهم داد که چگونه درختان مرکل با EdDSA کار می کنند

Setting up your current directory
? What project do you want to scaffold? EdDSA signature verification with Fixed Merkle Tree
? Choose the hashing algorithm to use:  poseidon
? Do you wish to add public inputs to sign? (E.g: address,amount) no

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

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

بنابراین با استفاده از پروژه بازسازی شد niftyzk init و پارامترهای مختلف را انتخاب کرد. و این همان است circuit.circom اکنون فایل کنید:

pragma circom 2.0.0;

include "../node_modules/circomlib/circuits/eddsaposeidon.circom";
include "../node_modules/circomlib/circuits/poseidon.circom";
include "./merkletree.circom";


template VerifySignature(levels){

    signal input message;
    signal input root;

    signal input pathIndices[levels];
    signal input pathElements[levels];


    //The parameters for the signature
    //The Ax and Ay parameters are the public key, Ax = pubKey[0], Ay = pubKey[1]
    signal input Ax;
    signal input Ay;
    signal input S;
    signal input R8x;
    signal input R8y;
    component eddsa = EdDSAPoseidonVerifier();

    component poseidon = Poseidon(1);
    poseidon.inputs[0] <== message;

    // Verify the signature on the message hash

    eddsa.enabled <== 1;
    eddsa.Ax <== Ax;
    eddsa.Ay <== Ay;
    eddsa.S <== S;
    eddsa.R8x <== R8x;
    eddsa.R8y <== R8y;
    eddsa.M <== poseidon.out;

    //We compute a public key by hashing Ax and Ay, 
   // this will be later used with the merkle tree
   component pubKeyHasher = Poseidon(2);
   pubKeyHasher.inputs[0] <== Ax;
   pubKeyHasher.inputs[1] <== Ay;

   //Verify the merkle tree
   component tree = MerkleTreeChecker(levels);


  for (var i = 0; i < levels; i++) {
      tree.pathElements[i] <== pathElements[i];
      tree.pathIndices[i] <== pathIndices[i];
  }
  tree.root <== root;
  tree.leaf <== pubKeyHasher.out; 
    }

component main {public [message,root]}  = VerifySignature(20); 

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

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

بنابراین می توانید ببینید که تفاوت هایی وجود دارد. من این بار پارامترهای اضافی بیشتری برای امضا اضافه نکردم، بنابراین فقط دو ورودی عمومی وجود دارد، message و root.

بنابراین root ریشه درخت مرکل است که می خواهیم بررسی کنیم که کلید عمومی که این پیام را امضا کرده است در داخل درخت وجود دارد. این یک روش کنترل دسترسی است، بنابراین ما می دانیم که امضا معتبر است و کلید عمومی امضاکننده مجاز است تأیید را انجام دهد زیرا کلید او در داخل درخت است.

آدرس را با استفاده از pubKeyHasher از Axe و Ay، می توانید ببینید که دقیقاً همان کد جاوا اسکریپت را که قبلاً نشان دادم انجام می دهد. را MerkleTreeChecker صحت را ادعا می کند و اگر برگ در ریشه نباشد شکست خواهد خورد.

pathIndices و pathElements اثبات merkle هستند. می توانید مدار درخت مرکل را در فایل کاوش کنید merkletree.circom در آموزش قبلی توضیح دادم.

بنابراین برای ایجاد دستی درختان مرکل با استفاده از cli، می توانیم استفاده کنیم npm run new که آدرس ها را تولید می کند و درختی را با استفاده از آنها به عنوان برگ ایجاد می کند.

CREATING A NEW MERKLE TREE

Enter how many accounts would you like to generate:
4
Generating accounts and addresses. Please wait!
Done
Generating merkle tree from addresses!
Done.Root is 21804813178957116268623645093306988559564490743632413729059072914509024611553 
Serializing data.
Writing to file.
Done

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

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

کلیدهای خصوصی برای حساب های مربوطه در قسمت قرار می گیرند private دایرکتوری در حالی که بقیه در قسمت قرار می گیرند public دایرکتوری اگر شما انتخاب کردید MiMC7 یا MiMCSponge سپس پارامتر کلید در dir خصوصی ذخیره می شود و برای تأیید مورد نیاز است، در غیر این صورت نیازی به استفاده از جزئیات خصوصی برای ایجاد یک اثبات merkle و تأیید آن ندارید.

پس بیایید فرار کنیم npm run proof و ریشه و نشانی می خواهد و برهان مرکل را بیرون می اندازد

سپس npm run verify یک ریشه و اثبات می خواهد و اگر همه چیز به درستی اجرا شود، اثبات معتبر خواهد بود.

می‌توانید از EdDSA برای هویت، جمع‌بندی، مقیاس‌بندی، رأی‌گیری یا انتزاع حساب استفاده کنید. بستگی به خودت داره 🙂

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

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

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

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