체인의정석

node.js에서 VRF 만들기 (node-ecvrf, drand-client 사용) 본문

개발/backend

node.js에서 VRF 만들기 (node-ecvrf, drand-client 사용)

체인의정석 2024. 11. 29. 13:55
728x90
반응형

VRF(Verifiable Random Function)는 검증 가능한 랜덤 함수로, 블록체인 환경에서 안전하고 예측 불가능한 난수를 생성하는 데 사용됩니다. VRF의 주요 특징은 다음과 같습니다:

  1. 예측 불가능성: 입력값과 비밀키를 모르는 사람은 출력을 예측할 수 없습니다.
  2. 검증 가능성: 공개키를 사용하여 출력의 정당성을 검증할 수 있습니다.
  3. 일관성: 동일한 입력과 비밀키에 대해 항상 같은 출력을 생성합니다.

@kenshi.io/node-ecvrf 모듈은 Node.js 환경에서 VRF 기능을 구현한 라이브러리입니다. 이 모듈의 주요 함수들은 다음과 같습니다:

  1. prove(secretKey, message): 주어진 비밀키와 메시지를 사용하여 VRF 증명을 생성합니다.
  2. verify(publicKey, message, proof): 공개키, 메시지, 증명을 사용하여 VRF 증명의 유효성을 검증합니다.
  3. proofToHash(proof): VRF 증명을 해시값으로 변환합니다.
  4. generateKeyPair(): VRF에 사용할 새로운 키 쌍(비밀키와 공개키)을 생성합니다.

먼저 VRF의 랜덤 시드의 경우 drand-client를 통해 가져올 수 있다. 랜덤 시드를 drand같은 신뢰할 수 있는 여러 주체들이 외부에서 api를 통해 리턴하는 값으로 두어야 제대로된 랜덤 값을 뽑을 수 있기 때문에 이와 같이 진행하였다.

1. 모듈 설치하기

npm install @kenshi.io/node-ecvrf --save
npm install drand-client --save

2. drand 사용하여 랜덤 시드 가져오기

    const chainHash = '1234e7a956ed2ffed73dbd7123123d6f289912540d7651336225dc172e511234';
    const publicKey = '1234005eb8e564ca0a47c8a77c1235309a4797127c71bc5cce96366b5d7a569937c529eeda66c7293784a94028011234';
    const options = {
        disableBeaconVerification: false,
        noCache: false,
        chainVerificationParams: { chainHash, publicKey }
    };
    const chain = new HttpCachingChain('https://api.drand.sh', options);
    const client = new HttpChainClient(chain, options);
    let round; //최근의 값을 저장해두고 업데이트 된 경우에만 사용되도록 하는 기능
    
    async function getRandom() {
        const beacon = await client.latest();
        if (round === beacon.round) {
            await new Promise(resolve => setTimeout(resolve, 1000));
            return await getRandom();
        }
        round = beacon.round;
        console.log("beacon.randomness >>", beacon.randomness);
        return beacon.randomness;
    }

다음과 같이 drand의 api로 부터 랜덤 시드를 뽑아낼 수 있다. 다만 해당 api의 경우 시드를 뽑아낼 때 시간이 좀 걸리기 때문에 여기서는 round라는 최근 값을 저장해두고 시드값으로 사용하였다.

3. vrf 생성하기

    const keypair = keygen();
    console.log("keypair.public_key.key : ", keypair.public_key.key);

    const alpha = await getRandom();
    console.log("alpha >>", alpha);

    const proof = prove(keypair.secret_key, alpha);
    console.log("proof : ", proof);

    const random = proofToHash(proof);
    console.log("random >>", random.toString('hex'));

    const verify_res = verify(keypair.public_key.key, proof, alpha); //이렇게 3개
    console.log("verify_res >>", verify_res);

다음과 같은 과정을 거치게 되면 verify를 하는것이 가능해지게 된다.

이 때 alpha를 시드 값인 drand의 응답값으로 두었으며, 이를 이용하여 proof를 생성하고 proof안에 개인키를 이용한 서명과 랜덤 시드값이 들어가 있기 때문에 이를 통해서 hash를 취해서 랜덤 값으로 취하면 사용가능한 vrf랜덤 값이 나오게 되며,
추후 verify를 사용하게 되면 랜덤 값에 대한 검증도 가능하게 된다.

참고로 키를 매번 생성하는 것이 아니라 블록체인 상의 개인키를 통해서 vrf서명을 해야하는 경우라면 다음과 같은 함수를 통해서 키를 도출할 수 있다.

	const elliptic = require("elliptic");
	const EC = new elliptic.ec("secp256k1");

    const ECVRF_keygen = (privateKey) => {
        // privateKey는 16진수 문자열로 입력받습니다.
        const keypair = EC.keyFromPrivate(privateKey, 'hex');
        const public_key = keypair.getPublic('hex');
        return {
          secret_key: privateKey,
          public_key: {
            key: public_key,
            compressed: keypair.getPublic(true, 'hex'),
            x: keypair.getPublic().getX().toString('hex'),
            y: keypair.getPublic().getY().toString('hex'),
          },
        };
      };

    const keypair = ECVRF_keygen("개인키");
    console.log("keypair.public_key.key : ", keypair.public_key.key);
    console.log("keypair.secret_key : ", keypair.secret_key);

 

728x90
반응형
Comments