[BlockChain] ZK (Cairo) - 나이 인증 회로 구현

2025. 7. 11. 19:18·개발/BlockChain
728x90
반응형

ZK [Cairo] - 나이 인증 회로 구현

모든 코드는 깃허브에서 볼수 있습니다.

(0) 흐름

zk 에서 핵심은 내 나이를 밝히지 않고 20세 이상인걸 만족하는지 증명하는 것이다.

먼저 나이와 nonce(랜덤값)을 받아서 해시값(commitment)을 만든다.

이 해시값을 조건(20세)과 함께 해시값(zk-proof)을 만든다.

컨트랙트에서 commitment 와 조건(20) 으로 해시값을 만들어서 zk-proof와 비교한다.

  • commitment는 공개될 값이므로 nonce가 없다면 dictionary attack 으로 내 나이를 알수 있다.

(1) 세팅

scarb new prover
scarb new verfier

이제는 scarb new 가 어느정도 자연스러워졌다.

의도는 prover 에서 age, nonce, min_age를 받아 zk-proof, commitment를 만든다.

그 zk-proof, commitment를 verifier에서 검증하도록 하는것이다.

해시를 만드는 과정은 오프체인에서 이루어지고 검증은 온체인에서 하도록 하는것이 목표이다.

원래는 2개의 기능을 하나의 프로젝트에 담고 싶었지만 prover 는 오프체인에서 gas가 필요없고 verifier는 온체인으로 gas 가 필요하다.

아직 이러한 방법들이 생소해서 두개로 나누어서 진행했다. 하나의 프로젝트로 가능한지는 모르겠음

(2) prover

prover 프로젝트 생성 후 모든 명령어는 prover 경로에서 실행한다.

cd prover

lib.cairo

use core::poseidon::PoseidonTrait;
use core::hash::HashStateTrait;

pub fn create_commitment(age: u32, nonce: felt252) -> felt252 {
    let mut state = PoseidonTrait::new();
    state = state.update(age.into());
    state = state.update(nonce);
    let commitment = state.finalize();

    commitment
}

pub fn generate_zk_proof(age: u32, nonce: felt252, min_age: u32, commitment: felt252) -> felt252 {
    if age < min_age {
        panic!("Age condition not satisfied");
    }

    let mut proof_state = PoseidonTrait::new();
    proof_state = proof_state.update(commitment);
    proof_state = proof_state.update(min_age.into());
    let zk_proof = proof_state.finalize();

    zk_proof
}

#[executable]
fn main(age: u32, min_age: u32, nonce: felt252) -> (felt252, felt252) {
    let commitment = create_commitment(age, nonce);
    let zk_proof = generate_zk_proof(age, nonce, min_age, commitment);

    println!("commitment: {}", commitment);
    println!("zk_proof: {}", zk_proof);

    (commitment, zk_proof)
}

Scarb.toml 은 로컬에서 오프체인으로 실행할것이므로 전에만든 prime number 의 toml 파일을 참고했다.

[package]
name = "prover"
version = "0.1.0"
edition = "2024_07"

[dependencies]
cairo_execute = "2.11.4"

[cairo]
enable-gas = false

[[target.executable]]
name = "main"
function = "prover::main"

build

scarb build

execute

scarb execute --print-program-output --arguments 25,20,12345

result

commitment: 2773162440671216239964217308338671978964196128669216495597811269554466628943
zk_proof: 3405247300715218403610915007102205477473161431843118660413475459999249699169
Program output:
-845340347994914973733105474756398126658911086662380204375280786581405391538
-213255487950912810086407775992864628149945783488478039559616596136622321312
Saving output to: target/execute/prover/execution1

print랑 output 이 달라도 당황하면 안된다. 난 매우 당황함

찾아보니 felt252 타입은 부호없는 unsigned 값이고 output 은 부호있는 signed integer 로 해석해서 그런다고 한다.

만약 나이가 최소나이보다 작다면 아예 실패한다.

scarb execute --print-program-output --arguments 18,20,54321

result

error: Panicked with "Age condition not satisfied".

(3) verifier

verifier는 컨트랙트로 작성할거고 위에서 만든 zk_proof, commitment, age를 받아서 검증한다.

cd ../verifier

lib.cairo

#[starknet::interface]
pub trait IAgeVerifier<TContractState> {
    fn verify_zk_proof(
        ref self: TContractState,
        commitment: felt252,
        zk_proof: felt252,
        min_age: u32
    ) -> bool;
}

#[starknet::contract]
mod AgeVerifier {
    use core::poseidon::PoseidonTrait;
    use core::hash::HashStateTrait;

    #[storage]
    struct Storage {
    }

    #[abi(embed_v0)]
    impl AgeVerifierImpl of super::IAgeVerifier<ContractState> {
        fn verify_zk_proof(
            ref self: ContractState,
            commitment: felt252,
            zk_proof: felt252,
            min_age: u32
        ) -> bool {
            let mut proof_state = PoseidonTrait::new();
            proof_state = proof_state.update(commitment);
            proof_state = proof_state.update(min_age.into());
            let calculated_proof = proof_state.finalize();

            zk_proof == calculated_proof
        }
    }
}

Local 배포

account 세팅 해주고 sncast.devnet 추가

declare (class hash 생성)

sncast --profile=devnet declare \
    --contract-name=AgeVerifier

result

[WARNING] Profile devnet does not exist in scarb, using 'release' profile.
command: declare
class_hash: 0x07889dd197fa9f08a61d5e7923d7d2d3fa3e6dda790055cc17665b4485ddd1ff
transaction_hash: 0x0584a3502bc2b44b0a28e2ce1654fe95bd2f3ed2bf092141a0197770b6d69153

To see declaration details, visit:
class: https://sepolia.starkscan.co/class/0x07889dd197fa9f08a61d5e7923d7d2d3fa3e6dda790055cc17665b4485ddd1ff
transaction: https://sepolia.starkscan.co/tx/0x0584a3502bc2b44b0a28e2ce1654fe95bd2f3ed2bf092141a0197770b6d69153

deploy

sncast --profile=devnet deploy \
    --class-hash=0x07889dd197fa9f08a61d5e7923d7d2d3fa3e6dda790055cc17665b4485ddd1ff \
    --salt=0

log

command: deploy
contract_address: 0x02b71035806970d86283b587ee1d614046d75895061a44d32785a1c26afb99e5
transaction_hash: 0x02a57fd86dfb4646107c71e68db35eeaf174764e4b9d4741c292c53621e1f5b1

To see deployment details, visit:
contract: https://sepolia.starkscan.co/contract/0x02b71035806970d86283b587ee1d614046d75895061a44d32785a1c26afb99e5
transaction: https://sepolia.starkscan.co/tx/0x02a57fd86dfb4646107c71e68db35eeaf174764e4b9d4741c292c53621e1f5b1

proof 검증 - 위에서 만든 commitment,zk-proof,나이조건(20) 을 넣어본다.

sncast --profile=devnet call \
    --contract-address=0x02b71035806970d86283b587ee1d614046d75895061a44d32785a1c26afb99e5 \
    --function=verify_zk_proof \
    --arguments 2773162440671216239964217308338671978964196128669216495597811269554466628943,3405247300715218403610915007102205477473161431843118660413475459999249699169,20

result

command: call
response: true
response_raw: [0x1]

그럼 나이조건을 19로 넣으면 어떻게 되나?

command: call
response: false
response_raw: [0x0]

20세 이상은 당연히 19세 이상인데 실패한다.

이것까지 만족시키게 짤수 있을까 고민해봐야겠다..

레퍼런스

  • 포세이돈 해시

  • starknet cairo book

728x90
반응형

'개발 > BlockChain' 카테고리의 다른 글

[Blockchain] kaiascan은 token transfer 를 어떻게 처리할까  (3) 2025.07.22
[Blockchain] ZK - noir, cairo 섞어쓰기  (0) 2025.07.13
[Blockchain] ZK - STARKNET SEPOLIA에 배포해보기  (0) 2025.07.06
[Blcokchain] ZK - Cairo 로 Prime Number(소수) 증명하기  (0) 2025.07.04
[Blockchain] ZK - Cairo 시작해보기  (1) 2025.07.04
'개발/BlockChain' 카테고리의 다른 글
  • [Blockchain] kaiascan은 token transfer 를 어떻게 처리할까
  • [Blockchain] ZK - noir, cairo 섞어쓰기
  • [Blockchain] ZK - STARKNET SEPOLIA에 배포해보기
  • [Blcokchain] ZK - Cairo 로 Prime Number(소수) 증명하기
TeTedo.
TeTedo.
  • TeTedo.
    TeTedo 개발 일기
    TeTedo.
  • 전체
    오늘
    어제
    • 분류 전체보기 (319)
      • 개발 (274)
        • Article (4)
        • 정리 (21)
        • Spring Boot (17)
        • JPA (2)
        • JAVA (6)
        • Database (4)
        • 자료구조 (11)
        • 알고리즘 (32)
        • React (20)
        • Docker (10)
        • node.js (18)
        • Devops (11)
        • Linux (4)
        • TypeScript (3)
        • Go (10)
        • HyperLedger (4)
        • BlockChain (43)
        • html, css, js (48)
        • CS (3)
        • AWS (3)
      • 모아두고 나중에 쓰기 (3)
      • 팀프로젝트 (18)
        • SNS(키보드워리어) (9)
        • close_sea (9)
      • 개인프로젝트 (1)
        • Around Flavor (1)
        • CHAM (13)
        • ethFruitShop (5)
      • 독서 (0)
        • 스프링부트와 AWS로 혼자 구현하는 웹 서비스 (0)
  • 블로그 메뉴

    • 홈
    • 개발일기
    • CS
    • 실습
    • 코딩테스트
    • 웹
    • Go
    • node.js
    • 팀플
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    명령어
    프로그래머스
    html
    30일챌린지
    node.js
    mysql
    30일 챌린지
    js
    go언어
    go
    하이퍼레저
    도커
    nodejs
    컨테이너
    erc20
    CSS
    node
    ERC721
    블록체인
    React
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
TeTedo.
[BlockChain] ZK (Cairo) - 나이 인증 회로 구현
상단으로

티스토리툴바