- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 1. 블록체인 기초: 해시, 머클 트리, 합의
- 2. 이더리움 내부: EVM, 가스, 상태 트리
- 3. 스마트 컨트랙트: Solidity 패턴과 보안
- 4. DeFi 프로토콜: AMM, 대출, MEV
- 5. ZK 증명: zk-SNARK, PLONK, ZK Rollup
- 6. AI + 블록체인
- 7. 개발 도구
- 퀴즈
- 마무리
1. 블록체인 기초: 해시, 머클 트리, 합의
1.1 해시 함수와 불변성
블록체인의 핵심은 암호학적 해시 함수입니다. 각 블록은 이전 블록의 해시를 포함하여 체인을 형성합니다.
import hashlib
import json
def compute_block_hash(index, previous_hash, timestamp, data, nonce):
block_content = json.dumps({
"index": index,
"previous_hash": previous_hash,
"timestamp": timestamp,
"data": data,
"nonce": nonce
}, sort_keys=True)
return hashlib.sha256(block_content.encode()).hexdigest()
# SHA-256 속성: 결정론적, 단방향, 눈사태 효과
hash1 = hashlib.sha256(b"hello").hexdigest()
hash2 = hashlib.sha256(b"hellO").hexdigest()
# 한 글자 차이로 완전히 다른 해시 생성
print(hash1[:16], "vs", hash2[:16])
1.2 머클 트리 (Merkle Tree)
머클 트리는 대규모 데이터셋의 무결성을 O(log n) 증명으로 검증합니다. 비트코인과 이더리움 모두 트랜잭션 검증에 활용합니다.
def build_merkle_tree(leaves):
if not leaves:
return None
layer = [hashlib.sha256(leaf.encode()).hexdigest() for leaf in leaves]
while len(layer) > 1:
if len(layer) % 2 != 0:
layer.append(layer[-1]) # 홀수면 마지막 복제
layer = [
hashlib.sha256((layer[i] + layer[i+1]).encode()).hexdigest()
for i in range(0, len(layer), 2)
]
return layer[0] # Merkle Root
txs = ["tx_alice_to_bob", "tx_carol_to_dave", "tx_eve_to_frank", "tx_grace_to_heidi"]
root = build_merkle_tree(txs)
print("Merkle Root:", root)
1.3 합의 알고리즘 비교
| 알고리즘 | 에너지 | 처리량 | 탈중앙화 | 대표 체인 |
|---|---|---|---|---|
| PoW | 매우 높음 | ~7 TPS | 높음 | Bitcoin |
| PoS | 낮음 | ~수천 TPS | 중간 | Ethereum |
| DPoS | 낮음 | ~수만 TPS | 낮음 | EOS, TRON |
| PBFT | 낮음 | ~수천 TPS | 낮음 | Hyperledger |
PoS 슬래싱: 이더리움 PoS에서 검증자가 이중 서명(double voting)하면 스테이킹한 ETH의 일부가 소각됩니다. 이 경제적 페널티가 보안의 핵심입니다.
2. 이더리움 내부: EVM, 가스, 상태 트리
2.1 EVM (Ethereum Virtual Machine)
EVM은 256비트 스택 기반 가상 머신으로, 모든 이더리움 노드에서 동일하게 실행되는 결정론적 환경입니다.
EVM 스택 연산 예시 (PUSH1, ADD, MUL):
PUSH1 0x03 → 스택: [3]
PUSH1 0x04 → 스택: [3, 4]
ADD → 스택: [7]
PUSH1 0x02 → 스택: [7, 2]
MUL → 스택: [14]
주요 EVM 저장소:
- Stack: 최대 1024개 요소, 임시 연산
- Memory: 바이트 배열, 함수 실행 중 임시 사용
- Storage: 키-값 영구 저장소 (가장 비쌈, SSTORE = 20,000 가스)
- Calldata: 외부 함수 호출 시 입력 데이터
2.2 EIP-1559 가스 메커니즘
EIP-1559 이전에는 가스 경매 방식이었습니다. 이후 기본 수수료(base fee)는 소각되고 팁(priority fee)만 검증자에게 전달됩니다.
# EIP-1559 트랜잭션 비용 계산
base_fee = 15 # gwei (네트워크 상태에 따라 자동 조정)
priority_fee = 2 # gwei (팁)
max_fee = 20 # gwei (최대 허용)
actual_fee = min(base_fee + priority_fee, max_fee)
gas_used = 21000 # 단순 ETH 전송
total_cost_gwei = actual_fee * gas_used
total_cost_eth = total_cost_gwei / 1e9
print(f"거래 수수료: {total_cost_eth:.6f} ETH")
# base_fee 소각 → ETH 디플레이션 효과
burned = base_fee * gas_used / 1e9
print(f"소각된 ETH: {burned:.6f} ETH")
2.3 The Merge: PoW에서 PoS로
2022년 9월 15일, 이더리움은 The Merge를 통해 에너지 소비를 약 99.95% 절감했습니다.
- 비콘 체인(Beacon Chain): 2020년 12월부터 병렬 운영된 PoS 체인
- 실행 레이어: 기존 EVM 상태와 트랜잭션 처리
- 합의 레이어: 검증자 관리, 어테스테이션, 최종성(finality)
- 최소 스테이킹: 32 ETH per validator
3. 스마트 컨트랙트: Solidity 패턴과 보안
3.1 ERC-20 토큰 with ReentrancyGuard
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract SecureToken is ERC20, ReentrancyGuard, Ownable {
uint256 public constant MAX_SUPPLY = 1_000_000 * 10**18;
mapping(address => uint256) public lockTime;
event TokensLocked(address indexed user, uint256 amount, uint256 unlockTime);
event TokensUnlocked(address indexed user, uint256 amount);
constructor() ERC20("SecureToken", "STKN") Ownable(msg.sender) {
_mint(msg.sender, 100_000 * 10**18);
}
// Checks-Effects-Interactions 패턴 적용
function lockAndRelease(uint256 amount, uint256 duration)
external
nonReentrant // 재진입 방어
{
// 1. Checks: 조건 검증
require(amount > 0, "Amount must be positive");
require(balanceOf(msg.sender) >= amount, "Insufficient balance");
require(duration >= 1 days && duration <= 365 days, "Invalid duration");
// 2. Effects: 상태 변경 (외부 호출 이전)
lockTime[msg.sender] = block.timestamp + duration;
_transfer(msg.sender, address(this), amount);
// 3. Interactions: 외부 호출 (상태 변경 이후)
emit TokensLocked(msg.sender, amount, lockTime[msg.sender]);
}
function unlock() external nonReentrant {
// Checks
require(block.timestamp >= lockTime[msg.sender], "Still locked");
uint256 contractBalance = balanceOf(address(this));
require(contractBalance > 0, "Nothing to unlock");
// Effects
lockTime[msg.sender] = 0;
// Interactions
_transfer(address(this), msg.sender, contractBalance);
emit TokensUnlocked(msg.sender, contractBalance);
}
function mint(address to, uint256 amount) external onlyOwner {
require(totalSupply() + amount <= MAX_SUPPLY, "Exceeds max supply");
_mint(to, amount);
}
}
3.2 재진입 공격 원리
The DAO 해킹(2016년, 360만 ETH 탈취)에서 악용된 패턴입니다.
// 취약한 컨트랙트 (절대 사용 금지)
contract VulnerableBank {
mapping(address => uint256) public balances;
function withdraw() external {
uint256 amount = balances[msg.sender];
// 상태 변경 전에 외부 호출 → 재진입 가능!
(bool success,) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
balances[msg.sender] = 0; // 너무 늦음
}
}
// 공격 컨트랙트
contract Attacker {
VulnerableBank public target;
receive() external payable {
if (address(target).balance >= 1 ether) {
target.withdraw(); // 재귀 호출로 반복 인출
}
}
}
3.3 Hardhat 테스트
const { expect } = require('chai')
const { ethers } = require('hardhat')
describe('SecureToken', function () {
let token, owner, alice, bob
beforeEach(async function () {
;[owner, alice, bob] = await ethers.getSigners()
const SecureToken = await ethers.getContractFactory('SecureToken')
token = await SecureToken.deploy()
await token.waitForDeployment()
})
it('재진입 공격을 방어해야 한다', async function () {
// alice에게 토큰 전송
await token.transfer(alice.address, ethers.parseEther('1000'))
expect(await token.balanceOf(alice.address)).to.equal(ethers.parseEther('1000'))
})
it('잠금 기간 전에는 언락 불가', async function () {
await token.transfer(alice.address, ethers.parseEther('100'))
await token.connect(alice).lockAndRelease(
ethers.parseEther('100'),
86400 // 1일
)
await expect(token.connect(alice).unlock()).to.be.revertedWith('Still locked')
})
it('Foundry 스타일 퍼즈 테스트 시뮬레이션', async function () {
const amounts = [1, 100, 1000].map((n) => ethers.parseEther(n.toString()))
for (const amount of amounts) {
await token.mint(alice.address, amount)
}
const total = amounts.reduce((a, b) => a + b, 0n)
expect(await token.balanceOf(alice.address)).to.equal(total)
})
})
3.4 Foundry forge 테스트
// test/SecureToken.t.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Test.sol";
import "../src/SecureToken.sol";
contract SecureTokenTest is Test {
SecureToken public token;
address public alice = makeAddr("alice");
function setUp() public {
token = new SecureToken();
token.transfer(alice, 1000 ether);
}
function testLockRequiresDuration() public {
vm.startPrank(alice);
vm.expectRevert("Invalid duration");
token.lockAndRelease(100 ether, 12 hours); // 1일 미만
vm.stopPrank();
}
// 퍼즈 테스트: 임의 금액으로 반복 검증
function testFuzz_LockAmount(uint256 amount) public {
amount = bound(amount, 1 ether, 1000 ether);
vm.startPrank(alice);
token.lockAndRelease(amount, 1 days);
assertEq(token.balanceOf(address(token)), amount);
vm.stopPrank();
}
}
4. DeFi 프로토콜: AMM, 대출, MEV
4.1 Uniswap v3 집중 유동성
v2의 x*y=k 공식은 전 가격 범위에 유동성을 균등 분산합니다. v3는 LP가 특정 가격 구간에 유동성을 집중시켜 자본 효율을 최대 4000배 향상시킵니다.
import math
# Uniswap v3 가격 계산 (tick 기반)
# tick = log(sqrt(price)) / log(sqrt(1.0001))
def tick_to_price(tick):
return 1.0001 ** tick
def price_to_tick(price):
return math.floor(math.log(price) / math.log(1.0001))
def calculate_liquidity_v3(amount0, amount1, price_current, price_lower, price_upper):
"""집중 유동성 포지션의 L(유동성) 계산"""
sqrt_p = math.sqrt(price_current)
sqrt_pa = math.sqrt(price_lower)
sqrt_pb = math.sqrt(price_upper)
if price_current <= price_lower:
# 전부 token0
L = amount0 * (sqrt_pa * sqrt_pb) / (sqrt_pb - sqrt_pa)
elif price_current >= price_upper:
# 전부 token1
L = amount1 / (sqrt_pb - sqrt_pa)
else:
# 혼합
L0 = amount0 * (sqrt_p * sqrt_pb) / (sqrt_pb - sqrt_p)
L1 = amount1 / (sqrt_p - sqrt_pa)
L = min(L0, L1)
return L
# ETH/USDC 풀 예시
# 현재 가격: 3000 USDC/ETH, 범위: 2500 ~ 3500
L = calculate_liquidity_v3(
amount0=1.0, # 1 ETH
amount1=3000.0, # 3000 USDC
price_current=3000,
price_lower=2500,
price_upper=3500
)
print(f"유동성 L: {L:.4f}")
4.2 MEV (Maximal Extractable Value)
MEV는 블록 생성자가 트랜잭션 순서를 조작해 추출할 수 있는 최대 가치입니다.
주요 MEV 유형:
- Sandwich Attack: 대형 스왑 전후에 매수/매도로 슬리피지 이익
- Arbitrage: 탈중앙 거래소 간 가격 차이 포착
- Liquidation: 담보 부족 포지션 청산으로 보상 획득
Flashbots: MEV 민주화를 위한 인프라
- mev-boost: 이더리움 검증자와 블록 빌더를 연결
- 공개 경매로 MEV 분배의 투명성 향상
- Dark forest(멤풀 공격) 완화
5. ZK 증명: zk-SNARK, PLONK, ZK Rollup
5.1 zk-SNARK 원리
zk-SNARK(Zero-Knowledge Succinct Non-interactive ARguments of Knowledge)는 증명자가 비밀 정보(witness)를 공개하지 않고 검증자에게 명제의 진실성을 증명합니다.
핵심 속성:
- 완전성(Completeness): 참인 명제는 항상 증명 가능
- 건전성(Soundness): 거짓 명제는 증명 불가
- 영지식성(Zero-knowledge): 증명 과정에서 비밀 누설 없음
// snarkjs를 활용한 간단한 ZK 증명 예시
// circuit: a * b = c (a, b를 공개하지 않고 곱셈 증명)
const snarkjs = require('snarkjs')
const fs = require('fs')
async function generateAndVerifyProof() {
// 1. witness 계산 (비공개 입력)
const input = {
a: 3, // 비밀
b: 11, // 비밀
c: 33, // 공개: a * b = c
}
// 2. 증명 생성 (proving key 필요)
const { proof, publicSignals } = await snarkjs.groth16.fullProve(
input,
'multiply.wasm',
'multiply_final.zkey'
)
console.log('공개 신호:', publicSignals) // [33]
console.log('증명 크기:', JSON.stringify(proof).length, 'bytes')
// 3. 검증 (verification key만 필요)
const vKey = JSON.parse(fs.readFileSync('verification_key.json'))
const isValid = await snarkjs.groth16.verify(vKey, publicSignals, proof)
console.log('증명 유효:', isValid) // true
// a=3, b=11이라는 사실은 증명 없이는 알 수 없음
}
generateAndVerifyProof()
5.2 ZK Rollup 아키텍처
ZK Rollup은 L2에서 수천 건의 트랜잭션을 처리하고, 유효성 증명(validity proof)만 L1에 게시합니다.
L2 트랜잭션 배치 처리:
[tx1, tx2, ..., tx1000]
↓ 증명자(prover)
ZK Proof (수백 bytes)
↓ L1 검증자
이더리움 메인넷 (가스 절약 99%+)
zkSync Era vs StarkNet:
- zkSync Era: EVM 호환 (Boojum 증명 시스템, PLONK 기반)
- StarkNet: Cairo 언어, STARK 증명 (양자 컴퓨팅 저항성)
- Polygon zkEVM: 타입 2 EVM 동등성
6. AI + 블록체인
6.1 탈중앙 AI 컴퓨팅
Gensyn: 분산 GPU 클러스터에서 AI 모델 훈련을 검증 가능하게 만드는 프로토콜.
- 훈련 태스크를 작은 서브태스크로 분할
- 각 서브태스크에 검증 가능한 증명 생성
- 가정용 GPU도 네트워크에 참여 가능
Bittensor (TAO): AI 모델 간 탈중앙 시장
- 서브넷별 특화 AI 태스크
- 검증자(validator)가 마이너(miner) 출력 평가
- 기여도 기반 TAO 토큰 보상
6.2 온체인 AI 검증
# FHE(완전 동형 암호화) 개념 예시
# 암호화된 상태에서 연산 수행
# 실제 구현: Microsoft SEAL, OpenFHE 사용
# 개념적 예시:
def fhe_inference_concept(encrypted_input, model_weights):
"""
FHE를 사용하면 입력 데이터를 복호화하지 않고
암호화된 상태로 AI 추론 수행 가능
- 의료 데이터: 환자 정보 비공개로 진단
- 금융 데이터: 개인정보 보호하며 신용 평가
"""
# 동형 연산 (덧셈, 곱셈이 암호화 상태에서 가능)
encrypted_result = homomorphic_matrix_multiply(
encrypted_input,
model_weights # 모델은 공개, 입력만 암호화
)
return encrypted_result
7. 개발 도구
7.1 web3.py로 컨트랙트 인터랙션
from web3 import Web3
import json
# Alchemy/Infura RPC 연결
w3 = Web3(Web3.HTTPProvider("https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"))
print("연결 상태:", w3.is_connected())
print("최신 블록:", w3.eth.block_number)
# ERC-20 잔액 조회
ERC20_ABI = [
{"inputs": [{"name": "account", "type": "address"}],
"name": "balanceOf",
"outputs": [{"name": "", "type": "uint256"}],
"type": "function"},
{"inputs": [],
"name": "symbol",
"outputs": [{"name": "", "type": "string"}],
"type": "function"}
]
USDC_ADDRESS = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
usdc = w3.eth.contract(
address=Web3.to_checksum_address(USDC_ADDRESS),
abi=ERC20_ABI
)
address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" # vitalik.eth
balance = usdc.functions.balanceOf(address).call()
symbol = usdc.functions.symbol().call()
print(f"{symbol} 잔액: {balance / 10**6:.2f}")
# 트랜잭션 전송 (서명 포함)
def send_erc20(private_key, to_address, amount_wei):
account = w3.eth.account.from_key(private_key)
nonce = w3.eth.get_transaction_count(account.address)
tx = usdc.functions.transfer(to_address, amount_wei).build_transaction({
"from": account.address,
"nonce": nonce,
"gas": 100000,
"maxFeePerGas": w3.to_wei(20, "gwei"),
"maxPriorityFeePerGas": w3.to_wei(2, "gwei"),
})
signed = w3.eth.account.sign_transaction(tx, private_key)
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
return tx_hash.hex()
7.2 ethers.js v6
import { ethers } from 'ethers'
// 프로바이더 설정
const provider = new ethers.JsonRpcProvider('https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY')
// ENS 이름 해석
const address = await provider.resolveName('vitalik.eth')
console.log('vitalik.eth →', address)
// 이벤트 구독 (실시간 Transfer 감지)
const ERC20_FILTER_ABI = ['event Transfer(address indexed from, address indexed to, uint256 value)']
const usdc = new ethers.Contract(
'0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
ERC20_FILTER_ABI,
provider
)
usdc.on('Transfer', (from, to, value, event) => {
const amount = ethers.formatUnits(value, 6)
if (parseFloat(amount) > 100000) {
console.log(`대형 USDC 이동: ${amount} USDC`)
console.log(`from: ${from} → to: ${to}`)
console.log(`tx: ${event.log.transactionHash}`)
}
})
퀴즈
Q1. 이더리움 PoS가 PoW보다 에너지 효율적이면서도 보안을 유지하는 경제적 메커니즘은 무엇인가요?
정답: 슬래싱(Slashing)을 통한 경제적 페널티 메커니즘
설명: PoW는 물리적 에너지(전기)를 소모해 공격 비용을 만들지만, PoS는 검증자가 32 ETH를 잠금 담보로 설정합니다. 이중 서명, 서라운딩 투표 등 악의적 행동 시 스테이킹된 ETH의 일부(최소 1/32)가 자동 소각됩니다. 전체 네트워크의 1/3 이상이 공모 공격을 시도하면 최대 100% 슬래싱이 발생해 공격자가 막대한 손실을 입습니다. 에너지 소비 없이 경제적 손실이라는 동일한 억제력을 구현합니다.
Q2. 재진입 공격이 스마트 컨트랙트에서 발생하는 패턴과 방어 방법은?
정답: Checks-Effects-Interactions 패턴 + ReentrancyGuard
설명: 재진입 공격은 외부 컨트랙트가 콜백(receive/fallback)을 통해 원본 함수를 반복 호출하는 방식입니다. 취약한 패턴은 잔액을 0으로 만들기 전에 ETH를 전송합니다. 방어 방법은 세 가지입니다: (1) Checks-Effects-Interactions — 상태 변경을 외부 호출 이전에 완료, (2) OpenZeppelin의 ReentrancyGuard — nonReentrant 수식어로 동일 함수 재진입 차단, (3) Pull Payment 패턴 — 컨트랙트가 밀어내는 대신 사용자가 직접 인출.
Q3. Uniswap v3의 집중 유동성이 v2보다 자본 효율이 높은 원리는?
정답: LP가 특정 가격 범위에 유동성을 집중시켜 활성 자본 비율을 극대화
설명: v2의 x*y=k 공식은 0부터 무한대까지 전 가격 범위에 유동성이 균등 배포됩니다. 실제 거래는 좁은 가격 구간에서 발생하므로 대부분의 자본이 유휴 상태입니다. v3에서 LP는 예상 가격 범위(예: 2500~3500 USDC/ETH)에만 자본을 배치합니다. 동일한 자본으로 해당 구간에서 최대 4000배 더 깊은 유동성을 제공하며, 수수료 수익도 집중됩니다. 단점은 가격이 범위를 벗어나면 수수료가 발생하지 않는다는 점입니다.
Q4. zk-SNARK에서 증명자가 지식을 노출하지 않고 검증자를 납득시키는 방법은?
정답: 다항식 약속(Polynomial Commitment)과 페어링 기반 암호학
설명: zk-SNARK는 계산을 다항식 방정식으로 변환(R1CS → QAP)합니다. 증명자는 비밀 witness를 직접 공개하는 대신, 타원 곡선 위의 점(Elliptic Curve Point)으로 인코딩한 다항식 평가값을 제출합니다. 검증자는 페어링(bilinear pairing) 연산으로 다항식 관계가 성립하는지 확인합니다. 이산 로그 문제의 어려움으로 인해 실제 witness를 역산할 수 없습니다. 증명 크기는 수백 바이트이며 검증은 밀리초 단위입니다.
Q5. MEV가 블록체인 생태계에 미치는 영향과 Flashbots의 역할은?
정답: MEV는 양면적 영향을 가지며, Flashbots는 투명한 경매로 부정적 효과를 완화
설명: MEV의 긍정적 측면은 차익거래를 통한 가격 효율화와 즉각적인 청산으로 DeFi 안정성 유지입니다. 부정적 측면은 샌드위치 공격으로 인한 일반 사용자 피해, 멤풀 혼잡 및 가스 전쟁, 체인 재구성(reorg) 인센티브입니다. Flashbots는 mev-boost를 통해 블록 빌더와 검증자를 분리하여 투명한 공개 경매 시장을 만들었습니다. 사용자는 번들 트랜잭션으로 프런트러닝을 방지하고, MEV 수익은 검증자와 공정하게 분배됩니다.
마무리
블록체인 엔지니어링은 암호학, 분산 시스템, 경제학이 교차하는 복잡한 분야입니다. 이더리움 생태계는 EVM 최적화, ZK 기술의 성숙, AI와의 융합으로 빠르게 진화하고 있습니다. 스마트 컨트랙트 보안은 코드 작성만큼이나 중요하며, Hardhat/Foundry 테스트와 공식 감사는 필수 과정입니다. ZK Rollup과 AI+블록체인의 결합은 Web3의 다음 단계를 정의할 것입니다.