Split View: 블록체인 & Web3 엔지니어 가이드: 스마트 컨트랙트, DeFi, ZK 증명, AI+블록체인까지
블록체인 & Web3 엔지니어 가이드: 스마트 컨트랙트, DeFi, ZK 증명, AI+블록체인까지
- 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의 다음 단계를 정의할 것입니다.
Blockchain & Web3 Engineer Guide: Smart Contracts, DeFi, ZK Proofs, and AI+Blockchain
- 1. Blockchain Fundamentals: Hashes, Merkle Trees, and Consensus
- 2. Ethereum Internals: EVM, Gas, and State Tree
- 3. Smart Contracts: Solidity Patterns and Security
- 4. DeFi Protocols: AMM, Lending, and MEV
- 5. ZK Proofs: zk-SNARK, PLONK, ZK Rollup
- 6. AI + Blockchain
- 7. Development Tools
- Quiz
- Conclusion
1. Blockchain Fundamentals: Hashes, Merkle Trees, and Consensus
1.1 Hash Functions and Immutability
The foundation of blockchain is the cryptographic hash function. Each block includes the hash of the previous block, forming a chain.
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 properties: deterministic, one-way, avalanche effect
hash1 = hashlib.sha256(b"hello").hexdigest()
hash2 = hashlib.sha256(b"hellO").hexdigest()
# A single character difference produces a completely different hash
print(hash1[:16], "vs", hash2[:16])
1.2 Merkle Tree
A Merkle tree verifies the integrity of large datasets with O(log n) proofs. Both Bitcoin and Ethereum use it for transaction verification.
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]) # duplicate last if odd
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 Consensus Algorithm Comparison
| Algorithm | Energy | Throughput | Decentralization | Representative Chain |
|---|---|---|---|---|
| PoW | Very High | ~7 TPS | High | Bitcoin |
| PoS | Low | ~Thousands TPS | Medium | Ethereum |
| DPoS | Low | ~Tens of Thousands TPS | Low | EOS, TRON |
| PBFT | Low | ~Thousands TPS | Low | Hyperledger |
PoS Slashing: In Ethereum PoS, if a validator double-signs (double votes), a portion of their staked ETH is burned. This economic penalty is the cornerstone of security.
2. Ethereum Internals: EVM, Gas, and State Tree
2.1 EVM (Ethereum Virtual Machine)
The EVM is a 256-bit stack-based virtual machine — a deterministic environment that executes identically on every Ethereum node.
EVM Stack Operation Example (PUSH1, ADD, MUL):
PUSH1 0x03 → Stack: [3]
PUSH1 0x04 → Stack: [3, 4]
ADD → Stack: [7]
PUSH1 0x02 → Stack: [7, 2]
MUL → Stack: [14]
Key EVM storage areas:
- Stack: Up to 1024 elements, temporary operations
- Memory: Byte array, temporary use during function execution
- Storage: Key-value persistent store (most expensive, SSTORE = 20,000 gas)
- Calldata: Input data for external function calls
2.2 EIP-1559 Gas Mechanism
Before EIP-1559, gas was auctioned. After the upgrade, the base fee is burned and only the tip (priority fee) goes to validators.
# EIP-1559 transaction cost calculation
base_fee = 15 # gwei (auto-adjusted based on network state)
priority_fee = 2 # gwei (tip)
max_fee = 20 # gwei (maximum allowed)
actual_fee = min(base_fee + priority_fee, max_fee)
gas_used = 21000 # simple ETH transfer
total_cost_gwei = actual_fee * gas_used
total_cost_eth = total_cost_gwei / 1e9
print(f"Transaction fee: {total_cost_eth:.6f} ETH")
# base_fee burned → ETH deflationary effect
burned = base_fee * gas_used / 1e9
print(f"Burned ETH: {burned:.6f} ETH")
2.3 The Merge: PoW to PoS
On September 15, 2022, Ethereum reduced energy consumption by approximately 99.95% through The Merge.
- Beacon Chain: The PoS chain that ran in parallel since December 2020
- Execution Layer: Handles existing EVM state and transaction processing
- Consensus Layer: Manages validators, attestations, and finality
- Minimum Staking: 32 ETH per validator
3. Smart Contracts: Solidity Patterns and Security
3.1 ERC-20 Token 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);
}
// Applies the Checks-Effects-Interactions pattern
function lockAndRelease(uint256 amount, uint256 duration)
external
nonReentrant // reentrancy defense
{
// 1. Checks: validate conditions
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: state changes (before external calls)
lockTime[msg.sender] = block.timestamp + duration;
_transfer(msg.sender, address(this), amount);
// 3. Interactions: external calls (after state changes)
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 How Reentrancy Attacks Work
This pattern was exploited in The DAO hack (2016, 3.6 million ETH stolen).
// Vulnerable contract (never use this)
contract VulnerableBank {
mapping(address => uint256) public balances;
function withdraw() external {
uint256 amount = balances[msg.sender];
// External call before state change → reentrancy possible!
(bool success,) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
balances[msg.sender] = 0; // too late
}
}
// Attacker contract
contract Attacker {
VulnerableBank public target;
receive() external payable {
if (address(target).balance >= 1 ether) {
target.withdraw(); // recursive call to drain repeatedly
}
}
}
3.3 Hardhat Tests
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('should defend against reentrancy attacks', async function () {
// Transfer tokens to alice
await token.transfer(alice.address, ethers.parseEther('1000'))
expect(await token.balanceOf(alice.address)).to.equal(ethers.parseEther('1000'))
})
it('should not allow unlock before lock period ends', async function () {
await token.transfer(alice.address, ethers.parseEther('100'))
await token.connect(alice).lockAndRelease(
ethers.parseEther('100'),
86400 // 1 day
)
await expect(token.connect(alice).unlock()).to.be.revertedWith('Still locked')
})
it('Foundry-style fuzz test simulation', 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 Tests
// 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); // less than 1 day
vm.stopPrank();
}
// Fuzz test: repeated verification with arbitrary amounts
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 Protocols: AMM, Lending, and MEV
4.1 Uniswap v3 Concentrated Liquidity
The v2 x*y=k formula distributes liquidity evenly across the entire price range. v3 allows LPs to concentrate liquidity within a specific price range, improving capital efficiency by up to 4000x.
import math
# Uniswap v3 price calculation (tick-based)
# 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):
"""Calculate L (liquidity) for a concentrated liquidity position"""
sqrt_p = math.sqrt(price_current)
sqrt_pa = math.sqrt(price_lower)
sqrt_pb = math.sqrt(price_upper)
if price_current <= price_lower:
# entirely token0
L = amount0 * (sqrt_pa * sqrt_pb) / (sqrt_pb - sqrt_pa)
elif price_current >= price_upper:
# entirely token1
L = amount1 / (sqrt_pb - sqrt_pa)
else:
# mixed
L0 = amount0 * (sqrt_p * sqrt_pb) / (sqrt_pb - sqrt_p)
L1 = amount1 / (sqrt_p - sqrt_pa)
L = min(L0, L1)
return L
# ETH/USDC pool example
# Current price: 3000 USDC/ETH, range: 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"Liquidity L: {L:.4f}")
4.2 MEV (Maximal Extractable Value)
MEV is the maximum value a block producer can extract by manipulating transaction ordering.
Key MEV Types:
- Sandwich Attack: Buying before and selling after a large swap to profit from slippage
- Arbitrage: Capturing price differences between decentralized exchanges
- Liquidation: Earning rewards by liquidating under-collateralized positions
Flashbots: Infrastructure for democratizing MEV
- mev-boost: Connects Ethereum validators with block builders
- Improves transparency of MEV distribution through open auctions
- Mitigates the dark forest (mempool attacks)
5. ZK Proofs: zk-SNARK, PLONK, ZK Rollup
5.1 How zk-SNARKs Work
zk-SNARK (Zero-Knowledge Succinct Non-interactive ARguments of Knowledge) allows a prover to convince a verifier of the truth of a statement without revealing secret information (the witness).
Core properties:
- Completeness: A true statement can always be proved
- Soundness: A false statement cannot be proved
- Zero-knowledge: No secrets are leaked during the proof
// Simple ZK proof example using snarkjs
// circuit: a * b = c (proving multiplication without revealing a and b)
const snarkjs = require('snarkjs')
const fs = require('fs')
async function generateAndVerifyProof() {
// 1. Compute witness (private input)
const input = {
a: 3, // secret
b: 11, // secret
c: 33, // public: a * b = c
}
// 2. Generate proof (requires proving key)
const { proof, publicSignals } = await snarkjs.groth16.fullProve(
input,
'multiply.wasm',
'multiply_final.zkey'
)
console.log('Public signals:', publicSignals) // [33]
console.log('Proof size:', JSON.stringify(proof).length, 'bytes')
// 3. Verify (only verification key needed)
const vKey = JSON.parse(fs.readFileSync('verification_key.json'))
const isValid = await snarkjs.groth16.verify(vKey, publicSignals, proof)
console.log('Proof valid:', isValid) // true
// Without the proof, a=3 and b=11 cannot be deduced
}
generateAndVerifyProof()
5.2 ZK Rollup Architecture
A ZK Rollup processes thousands of transactions on L2 and posts only a validity proof to L1.
L2 transaction batch processing:
[tx1, tx2, ..., tx1000]
↓ prover
ZK Proof (hundreds of bytes)
↓ L1 verifier
Ethereum Mainnet (99%+ gas savings)
zkSync Era vs StarkNet:
- zkSync Era: EVM-compatible (Boojum proving system, PLONK-based)
- StarkNet: Cairo language, STARK proofs (quantum computing resistant)
- Polygon zkEVM: Type 2 EVM equivalence
6. AI + Blockchain
6.1 Decentralized AI Computing
Gensyn: A protocol that makes AI model training verifiable on distributed GPU clusters.
- Splits training tasks into smaller sub-tasks
- Generates verifiable proofs for each sub-task
- Enables consumer GPUs to participate in the network
Bittensor (TAO): A decentralized marketplace for AI models
- Specialized AI tasks per subnet
- Validators evaluate miner outputs
- Contribution-based TAO token rewards
6.2 On-Chain AI Verification
# Conceptual example of FHE (Fully Homomorphic Encryption)
# Performing computation on encrypted data
# Real implementations: Microsoft SEAL, OpenFHE
# Conceptual example:
def fhe_inference_concept(encrypted_input, model_weights):
"""
With FHE, AI inference can be performed on encrypted input data
without decryption:
- Medical data: diagnosis without exposing patient information
- Financial data: credit scoring with privacy preserved
"""
# Homomorphic operations (addition and multiplication possible in encrypted state)
encrypted_result = homomorphic_matrix_multiply(
encrypted_input,
model_weights # model is public, only input is encrypted
)
return encrypted_result
7. Development Tools
7.1 Smart Contract Interaction with web3.py
from web3 import Web3
import json
# Alchemy/Infura RPC connection
w3 = Web3(Web3.HTTPProvider("https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"))
print("Connected:", w3.is_connected())
print("Latest block:", w3.eth.block_number)
# ERC-20 balance query
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: {balance / 10**6:.2f}")
# Send transaction (with signing)
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'
// Provider setup
const provider = new ethers.JsonRpcProvider('https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY')
// ENS name resolution
const address = await provider.resolveName('vitalik.eth')
console.log('vitalik.eth →', address)
// Event subscription (real-time Transfer detection)
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(`Large USDC transfer: ${amount} USDC`)
console.log(`from: ${from} → to: ${to}`)
console.log(`tx: ${event.log.transactionHash}`)
}
})
Quiz
Q1. What is the economic mechanism that allows Ethereum PoS to maintain security while being more energy-efficient than PoW?
Answer: The economic penalty mechanism through Slashing
Explanation: PoW consumes physical energy (electricity) to create attack costs, whereas PoS requires validators to lock up 32 ETH as collateral. For malicious behavior such as double signing or surround voting, a portion of the staked ETH (at minimum 1/32) is automatically burned. If more than one-third of the entire network attempts a coordinated attack, up to 100% slashing occurs, causing catastrophic losses for attackers. This achieves the same deterrent effect as PoW — through economic loss rather than energy consumption.
Q2. What is the pattern by which reentrancy attacks occur in smart contracts, and how can they be defended against?
Answer: Checks-Effects-Interactions pattern + ReentrancyGuard
Explanation: A reentrancy attack is when an external contract repeatedly calls the original function via a callback (receive/fallback). The vulnerable pattern sends ETH before zeroing the balance. There are three defenses: (1) Checks-Effects-Interactions — complete all state changes before any external call; (2) OpenZeppelin's ReentrancyGuard — the nonReentrant modifier blocks reentry into the same function; (3) Pull Payment pattern — instead of the contract pushing funds, users withdraw directly.
Q3. Why is Uniswap v3's concentrated liquidity more capital-efficient than v2?
Answer: LPs concentrate liquidity within a specific price range, maximizing the ratio of active capital
Explanation: v2's x*y=k formula distributes liquidity evenly from 0 to infinity across all price ranges. Since actual trades occur in a narrow price band, most capital sits idle. In v3, LPs deploy capital only within an anticipated price range (e.g., 2500–3500 USDC/ETH). With the same capital, this provides up to 4000x deeper liquidity within that range, and fee revenue is also concentrated there. The downside is that no fees are earned when the price moves outside the range.
Q4. How does a prover in zk-SNARK convince a verifier without revealing the knowledge?
Answer: Polynomial Commitments and pairing-based cryptography
Explanation: zk-SNARKs transform a computation into polynomial equations (R1CS → QAP). Instead of revealing the secret witness directly, the prover submits polynomial evaluations encoded as points on an elliptic curve. The verifier uses a bilinear pairing operation to confirm that the polynomial relationships hold. The hardness of the discrete logarithm problem makes it impossible to reverse-engineer the actual witness. Proof sizes are a few hundred bytes and verification takes milliseconds.
Q5. What are the effects of MEV on the blockchain ecosystem, and what role does Flashbots play?
Answer: MEV has dual effects; Flashbots mitigates the negatives through a transparent auction
Explanation: The positive side of MEV includes price efficiency through arbitrage and maintaining DeFi stability through timely liquidations. The negative side includes harm to ordinary users from sandwich attacks, mempool congestion and gas wars, and incentives for chain reorganization (reorgs). Flashbots, through mev-boost, separates block builders from validators to create a transparent open-auction market. Users can prevent front-running with bundled transactions, and MEV revenue is fairly distributed to validators.
Conclusion
Blockchain engineering is a complex field at the intersection of cryptography, distributed systems, and economics. The Ethereum ecosystem is rapidly evolving through EVM optimization, the maturation of ZK technology, and convergence with AI. Smart contract security is as important as writing the code itself — Hardhat/Foundry tests and formal audits are essential steps. The combination of ZK Rollups and AI+blockchain will define the next phase of Web3.