이번 주는 기존의 방식에서 벗어나, 직접 프로덕트를 만들어 보는 시간을 가졌다. 모든 조원들이 각자의 관심분야에 대하여 구현했고, 나는 계좌 정보 없이 잔고를 보여주는 방법을 고민해 보았다.
이 외에도 Turing Complete 한 Circom Based VM,
Anonymous Voting, Auction, Code Auditing without revealing the code 등 흥미로운 주제가 많았다.
하루 동안 완성하기에는 어려웠지만, 모두들 지금까지의 이론 공부에만 지쳐(?) 있다가 새로운 자극에 필요한 시점에 적절한 활동이었던 것 같다. 간만에 다들 아이패드를 놓고 노트북을 꺼내 코딩을 하니 눈빛이 살아나는 느낌이었다.
내가 구현하고자 하는 시나리오는 다음과 같다.
Alice 와 Bob이 NFT 거래를 (혹은 모종의 거래를) 원한다. Alice 는 Bob이 기준치 이상 (ex, 5 ETH) 의 잔고가 있어야 대화를 지속하고자 한다. Bob 은 Wallet 주소를 공개하고 싶지 않다. 하지만 꼭 5 ETH 이상을 보유하고 있으며, 이를 관리하는 Secret Key 도 보유하고 있다는 것을 증명하고 싶다.
const { ethers } = require('ethers');
const { Web3 } = require('web3');
// Replace with your private key
const walletSecretKey = 'YOUR_KEY';
// Create a wallet instance using the private key
const wallet = new ethers.Wallet(walletSecretKey);
// Message to sign
const message = `I am proving ownership of wallet and want to negotiate with Alice ${wallet.address}`;
async function signMessage() {
// Sign the message
const signature = await wallet.signMessage(message);
console.log("Message:", message);
console.log("Signature:", signature);
console.log("Address:", wallet.address);
return signature;
}
// Function to verify the signed message
function verifySignature(message, signature) {
// Recover the address from the signature
const recoveredAddress = ethers.verifyMessage(message, signature);
console.log("Recovered Address:", recoveredAddress);
// Compare the recovered address with the expected address
if (recoveredAddress.toLowerCase() === wallet.address.toLowerCase()) {
console.log("Signature is valid. The message was signed by:", recoveredAddress);
} else {
console.log("Signature verification failed. Recovered address:", recoveredAddress);
}
}
// Function to get the balance of the wallet
async function getBalance() {
// Connect to an Ethereum node (e.g., Infura)
const web3 = new Web3('YOUR-INFURA-KEY');
try {
// Get the balance in Wei (the smallest unit of Ether)
const balance = await web3.eth.getBalance(wallet.address);
// Convert the balance from Wei to Ether
const balanceInEth = web3.utils.fromWei(balance, 'ether');
console.log(`Balance of ${wallet.address}: ${balanceInEth} ETH`);
return balanceInEth;
} catch (error) {
console.error(`Error fetching balance: ${error}`);
}
}
// Main function to execute all steps
async function main() {
// Step 1: Sign the message
const signature = await signMessage();
// Step 2: Verify the signed message
verifySignature(message, signature);
// Step 3: Get and display the wallet balance
await getBalance();
}
// Execute the main function
main();
Run the script using Node.js:
node getBalance.js
이 과정에서 Message는 Alice가 Bob에게 Challange를 주는 방식으로 사용되며, (Non-Interactive 하게는 Random Hash로 대체될 수 있다. )
시간 안에 구현하지는 못했지만, 최종적으로 출력되는 Balance값을 받고, Alice의 요구사항 (ex: 5 ETH)보다 같거나 크다는 것이 증명되면 OK를 내보낸다 (Snark.js)
이 모든 것을 StreamLine화 할 수 있도록 App.js를 만들어 Bob 과 Alice에게 차례로 부여한다. Bob은 Secret Key를 입력하고, Alice는 요구사항 (5 ETH)과 Challange Message를 입력한다.
Alice가 입력을 마치고 Enter를 누르면, Snark OK와 함께 본인의 Message가 함께 출력된다.
첫 과정이다 보니 부족한 점이 많은데, 아직 실제로 구현된 적이 없는 Service라는 점에서 보람을 느꼈다. Circom까지 작업을 해보려 했는데 ... Circom은 아직 나에게는 어렵다. 또한 Ethers.js 라이브러리를 사용한다는 점에서 정합성을 100% 보장하기도 어렵지만, 향후 Javascript까지 실행 가능한 ZK VM이 있다면 한번 시도해 보고 싶다.
현재까지 개발된 많은 ZK 기법들이 어떤 고민을 거쳐 현재의 이르게 되었는지 대략이나마 가늠해 볼 수 있었던 소중한 시간이었다.