Domain: Blockchain & Web3
Blockchain không phải là database tốt hơn PostgreSQL. Web3 không phải là backend tốt hơn REST API. Nhưng khi bạn cần trustless coordination giữa các bên không tin nhau, không có central authority, và cần immutable audit trail — blockchain là công nghệ duy nhất làm được.
Section này không hype coin. Mục tiêu là hiểu sâu cơ chế hoạt động, trade-offs, và khi nào (và khi nào KHÔNG) nên dùng blockchain.
1. Blockchain Fundamentals
1.1 Cấu trúc Data Structure
Block Structure:
┌─────────────────────────────────────────┐
│ Block #1234 │
├─────────────────────────────────────────┤
│ Header: │
│ - Previous Hash: 0x7a3f8b... │
│ - Merkle Root: 0x9c2e1d... │
│ - Timestamp: 1713312000 │
│ - Nonce: 482756394 │
│ - Difficulty: 0000000000001a... │
├─────────────────────────────────────────┤
│ Transactions: │
│ [Tx1, Tx2, ..., TxN] │
└─────────────────────────────────────────┘
│ hash
▼
Block Hash: 0x0000000000001f3a...
│
▼ (becomes Previous Hash của block tiếp theo)
Block #1235
Merkle Tree trong block:
Merkle Root
/ \
H(AB) H(CD)
/ \ / \
H(A) H(B) H(C) H(D)
| | | |
Tx1 Tx2 Tx3 Tx4
Lợi ích: Verify 1 transaction mà không cần download toàn bộ block
→ SPV (Simplified Payment Verification) clients
package blockchain
import (
"crypto/sha256"
"encoding/hex"
"time"
)
type Block struct {
Index int64
Timestamp int64
Transactions []Transaction
PrevHash string
Hash string
Nonce int64
Difficulty int
}
type Transaction struct {
ID string
From string
To string
Amount int64
Signature string
}
func (b *Block) CalculateHash() string {
record := fmt.Sprintf("%d%d%s%s%d",
b.Index, b.Timestamp, b.PrevHash, b.MerkleRoot(), b.Nonce)
h := sha256.New()
h.Write([]byte(record))
return hex.EncodeToString(h.Sum(nil))
}
func (b *Block) MerkleRoot() string {
if len(b.Transactions) == 0 {
return ""
}
var hashes []string
for _, tx := range b.Transactions {
hashes = append(hashes, tx.Hash())
}
// Build Merkle tree bottom-up
for len(hashes) > 1 {
var newLevel []string
for i := 0; i < len(hashes); i += 2 {
if i+1 < len(hashes) {
combined := hashes[i] + hashes[i+1]
} else {
combined := hashes[i] + hashes[i] // duplicate if odd
}
h := sha256.Sum256([]byte(combined))
newLevel = append(newLevel, hex.EncodeToString(h[:]))
}
hashes = newLevel
}
return hashes[0]
}
1.2 Proof of Work — Mining
Mục tiêu: Tìm nonce sao cho hash(block_header + nonce) < target
Target = 2^(256 - difficulty)
Ví dụ difficulty = 20:
Hash phải bắt đầu bằng 20 bits 0 (khoảng 5 ký tự hex 0)
0x00000abc... → valid
0x0001abcd... → invalid
func (b *Block) Mine(difficulty int) {
target := strings.Repeat("0", difficulty)
for {
b.Nonce++
hash := b.CalculateHash()
if strings.HasPrefix(hash, target) {
b.Hash = hash
fmt.Printf("Block mined! Nonce: %d, Hash: %s\n", b.Nonce, hash)
return
}
// In thực tế, check every 100k iterations để update timestamp
if b.Nonce%100000 == 0 {
b.Timestamp = time.Now().Unix()
}
}
}
Tại sao PoW cần thiết?
Nếu không có PoW, attacker tạo ra 1000 blocks trong 1 giây → spam network. PoW làm việc tạo block "expensive" → chỉ honest miners có động lực mine (block reward).
Trade-off: Tốn điện khủng khiếp (Bitcoin: ~120 TWh/năm = tiêu thụ điện của cả Argentina).
2. Consensus Mechanisms
2.1 Proof of Work (PoW)
Bitcoin, Ethereum (trước The Merge):
Miners compete để giải puzzle (find nonce)
→ First to solve broadcasts block
→ Other nodes verify và accept
→ Miner nhận block reward (6.25 BTC cho Bitcoin)
Longest chain wins (chain với most cumulative difficulty)
51% Attack:
Nếu attacker có >50% hash power → có thể rewrite history
Nhưng economic disincentive: Cost of attack > benefit
2.2 Proof of Stake (PoS)
Ethereum (sau The Merge), Cardano, Polkadot:
Validators stake (lock) tokens
→ Random selection dựa trên stake amount + other factors
→ Validator propose block, others attest
→ Nếu validator act maliciously → slash (burn) stake
No mining → energy efficient (99.95% giảm so với PoW)
"Nothing at stake" problem:
Nếu có fork, validators có thể vote cho cả 2 chains (no cost)
→ Giải pháp: slashing conditions nếu vote conflicting
# Simplified PoS validator selection
import random
class PoSConsensus:
def __init__(self):
self.validators = {} # {address: stake_amount}
def stake(self, address, amount):
if address not in self.validators:
self.validators[address] = 0
self.validators[address] += amount
def select_validator(self):
"""
Weighted random selection dựa trên stake
Validator có stake 10 ETH có 10x chance so với 1 ETH
"""
total_stake = sum(self.validators.values())
if total_stake == 0:
return None
# Weighted random
rand = random.uniform(0, total_stake)
cumulative = 0
for address, stake in self.validators.items():
cumulative += stake
if rand <= cumulative:
return address
return None
2.3 Practical Byzantine Fault Tolerance (PBFT)
Dùng trong Hyperledger Fabric, private blockchains:
Assume N validators, f Byzantine (malicious) nodes
System works nếu N >= 3f + 1 (e.g., 4 nodes tolerate 1 Byzantine)
Phases:
1. Pre-prepare: Leader broadcasts proposal
2. Prepare: Nodes broadcast "I received proposal X"
3. Commit: Nếu node nhận 2f+1 prepare messages → broadcast commit
4. Reply: Nếu node nhận 2f+1 commit messages → execute
Fast finality (1-2 seconds) nhưng không scale (communication overhead O(N²))
→ Chỉ dùng cho consortium blockchains với <100 validators
3. Account Models: UTXO vs Account-based
3.1 UTXO (Bitcoin)
UTXO = Unspent Transaction Output
Alice có 10 BTC từ một UTXO trước đó
Alice muốn gửi 3 BTC cho Bob
Transaction:
Inputs:
- UTXO_1: 10 BTC (từ Alice)
Outputs:
- UTXO_2: 3 BTC (to Bob)
- UTXO_3: 6.99 BTC (change to Alice)
- Fee: 0.01 BTC (to miner)
UTXO_1 bị "spent" → không thể dùng lại (double-spend prevention)
Ưu điểm UTXO:
- Parallelism: Transactions không conflict nếu không dùng chung UTXO
- Privacy: Mỗi transaction tạo new address (change address)
- Stateless verification: Chỉ cần verify UTXO existence, không cần account balance
Nhược điểm:
- Phức tạp cho smart contracts (Ethereum không dùng UTXO vì lý do này)
- UTXO set grows indefinitely → disk space
3.2 Account-based (Ethereum)
Account có state:
- Balance: 100 ETH
- Nonce: 42 (số lượng transactions sent)
- Code: (nếu là smart contract)
- Storage: (state variables của contract)
Transaction:
From: Alice (nonce = 42)
To: Bob
Value: 10 ETH
Gas: 21000
Execution:
1. Verify Alice.balance >= 10 ETH + gas
2. Verify Alice.nonce == 42 (prevent replay)
3. Deduct Alice.balance -= 10 ETH
4. Increment Alice.nonce = 43
5. Add Bob.balance += 10 ETH
Ưu điểm Account-based:
- Đơn giản hơn cho smart contracts
- Dễ track balance (không cần scan UTXOs)
Nhược điểm:
- Sequential nonce → không thể parallel transactions từ cùng account
- Privacy kém (reuse address)
4. Smart Contracts — Ethereum Deep Dive
4.1 Solidity Basics
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SimpleToken {
mapping(address => uint256) public balances;
uint256 public totalSupply;
event Transfer(address indexed from, address indexed to, uint256 value);
constructor(uint256 _initialSupply) {
balances[msg.sender] = _initialSupply;
totalSupply = _initialSupply;
}
function transfer(address _to, uint256 _amount) public returns (bool) {
require(balances[msg.sender] >= _amount, "Insufficient balance");
require(_to != address(0), "Invalid recipient");
balances[msg.sender] -= _amount;
balances[_to] += _amount;
emit Transfer(msg.sender, _to, _amount);
return true;
}
}
4.2 Gas Mechanism
Gas = đơn vị đo computation cost
Mọi opcode có gas cost:
ADD: 3 gas
MUL: 5 gas
SSTORE (write to storage): 20,000 gas (first write), 5,000 gas (update)
SLOAD (read from storage): 200 gas
Transaction gas:
Gas Limit: 21,000 (simple transfer) to millions (complex contract)
Gas Price: user bid (gwei per gas)
Max Fee: Gas Limit × Gas Price
Nếu execution vượt Gas Limit → revert (nhưng vẫn trả gas đã dùng)
Tại sao cần gas?
Prevent infinite loops / DoS attacks. Mọi computation phải "pay".
// Ví dụ bad practice: unbounded loop
function badFunction(uint256[] memory data) public {
for (uint i = 0; i < data.length; i++) { // ← nếu data.length = 1M?
// process
}
}
// Good practice: bounded hoặc paginated
function goodFunction(uint256 start, uint256 end) public {
require(end - start <= 100, "Max 100 items per call");
for (uint i = start; i < end; i++) {
// process
}
}
4.3 EVM Storage Layout
Storage (persistent, on-chain):
- Slot-based: 2^256 slots, mỗi slot 32 bytes
- Expensive: SSTORE cost 20k gas
- Dùng cho state variables
Memory (temporary, in-transaction):
- Byte-addressed array
- Cheaper: 3 gas per word + expansion cost
- Reset về 0 sau transaction
Calldata (read-only, in-transaction):
- Input data của transaction
- Cheapest: 4 gas per zero byte, 16 gas per non-zero byte
- Dùng cho function parameters
Optimization pattern:
// Bad: multiple storage reads
function badSum() public view returns (uint256) {
return storageVar1 + storageVar2 + storageVar3; // 3 SLOADs = 600 gas
}
// Good: cache to memory
function goodSum() public view returns (uint256) {
uint256 a = storageVar1; // 1 SLOAD
uint256 b = storageVar2; // 1 SLOAD
uint256 c = storageVar3; // 1 SLOAD
return a + b + c; // memory operations: cheap
}
// Better: pack storage variables
contract Optimized {
// Packed into 1 slot (32 bytes)
uint128 public var1; // 16 bytes
uint64 public var2; // 8 bytes
uint64 public var3; // 8 bytes
// Total: 1 SLOAD to read all 3 → 200 gas instead of 600
}
5. DeFi — Decentralized Finance
5.1 Automated Market Maker (AMM)
Uniswap V2 constant product formula:
x × y = k
Pool: ETH/USDC
- Reserve ETH: 100
- Reserve USDC: 200,000
- k = 100 × 200,000 = 20,000,000
User swap 10 ETH:
New ETH reserve: 100 + 10 = 110
New USDC reserve: k / 110 = 20,000,000 / 110 ≈ 181,818
USDC out: 200,000 - 181,818 = 18,182 USDC
Effective price: 18,182 / 10 = 1,818 USDC per ETH
(với slippage vì large trade impacts pool)
contract SimpleAMM {
uint256 public reserveA;
uint256 public reserveB;
function swap(uint256 amountAIn) external returns (uint256 amountBOut) {
require(amountAIn > 0, "Invalid amount");
uint256 k = reserveA * reserveB;
uint256 newReserveA = reserveA + amountAIn;
uint256 newReserveB = k / newReserveA;
amountBOut = reserveB - newReserveB;
// Apply 0.3% fee
amountBOut = (amountBOut * 997) / 1000;
reserveA = newReserveA;
reserveB = reserveB - amountBOut;
// Transfer tokens (simplified)
return amountBOut;
}
}
5.2 Lending Protocol (Aave-style)
CollateralFactor = 80% (user có thể borrow tới 80% collateral value)
Alice deposits 10 ETH ($20k)
→ Collateral value: $20k × 80% = $16k
→ Alice có thể borrow max $16k stablecoins
Liquidation:
Nếu ETH price drops → collateral value < borrowed value
→ Liquidator có thể repay debt và claim collateral (với discount)
Liquidation threshold: 85%
If borrow / collateral > 85% → liquidatable
contract LendingPool {
mapping(address => uint256) public deposits; // ETH deposited
mapping(address => uint256) public borrows; // USDC borrowed
uint256 constant COLLATERAL_FACTOR = 80;
uint256 constant LIQUIDATION_THRESHOLD = 85;
function borrow(uint256 amount) external {
uint256 maxBorrow = (deposits[msg.sender] * getETHPrice() * COLLATERAL_FACTOR) / 100;
require(borrows[msg.sender] + amount <= maxBorrow, "Insufficient collateral");
borrows[msg.sender] += amount;
// Transfer USDC to user
}
function liquidate(address user) external {
uint256 collateralValue = deposits[user] * getETHPrice();
uint256 borrowValue = borrows[user];
uint256 healthFactor = (collateralValue * 100) / borrowValue;
require(healthFactor < LIQUIDATION_THRESHOLD, "Not liquidatable");
// Liquidator repays debt, gets collateral + bonus
uint256 collateralToLiquidator = (borrowValue * 105) / getETHPrice(); // 5% bonus
deposits[user] -= collateralToLiquidator;
borrows[user] = 0;
// Transfer collateral to liquidator
}
function getETHPrice() internal view returns (uint256) {
// Oracle call (Chainlink, etc.)
return 2000; // $2000 per ETH
}
}
5.3 Flash Loans
Borrow millions $ trong 1 transaction, miễn là repay before transaction ends
Use cases:
- Arbitrage giữa exchanges
- Collateral swap (repay loan A, take loan B)
- Liquidation (borrow to liquidate position, repay immediately)
contract FlashLoan {
function flashLoan(uint256 amount) external {
uint256 balanceBefore = token.balanceOf(address(this));
// Lend tokens to caller
token.transfer(msg.sender, amount);
// Caller executes arbitrary logic
IFlashLoanReceiver(msg.sender).executeOperation(amount);
// Check repayment + fee (0.09%)
uint256 fee = (amount * 9) / 10000;
uint256 balanceAfter = token.balanceOf(address(this));
require(balanceAfter >= balanceBefore + fee, "Flash loan not repaid");
}
}
// Attacker contract
contract FlashLoanAttacker is IFlashLoanReceiver {
function executeOperation(uint256 amount) external override {
// 1. Borrow 1M USDC from flash loan
// 2. Swap USDC → ETH on DEX A (price impact)
// 3. Swap ETH → USDC on DEX B (profit from arbitrage)
// 4. Repay flash loan + fee
// 5. Keep profit
}
}
Rủi ro: Flash loan attacks đã exploit nhiều DeFi protocols (e.g., bZx hack $1M).
6. Layer 2 Scaling Solutions
6.1 State Channels
Opening channel:
Alice và Bob lock 10 ETH each vào channel contract (on-chain tx)
Off-chain transactions:
Alice ← 5 ETH → Bob (sign off-chain, instant, no gas)
Alice ← 3 ETH → Bob (sign off-chain)
...
(1000 transactions off-chain)
Closing channel:
Submit latest state to contract → distribute funds on-chain
Challenge period:
Nếu Bob submit old state (cheating), Alice có 24h để submit newer state
→ Slash Bob's deposit
Use case: Lightning Network (Bitcoin), Raiden (Ethereum). Limitation: Requires locking capital, only works between channel participants.
6.2 Rollups
Optimistic Rollup (Optimism, Arbitrum):
- Assume transactions valid by default
- Fraud proof: Nếu có người cheat, submit proof to L1
- Challenge period: 7 days (slow withdrawals)
ZK-Rollup (zkSync, StarkNet):
- Generate zero-knowledge proof cho batch transactions
- Submit proof to L1 (very small, ~1KB for 1000 txs)
- No challenge period (instant finality)
- Trade-off: Expensive proof generation (need specialized hardware)
Throughput comparison:
Ethereum L1: ~15 TPS
Optimistic Rollup: ~2,000 TPS
ZK-Rollup: ~3,000 TPS
(Với gas cost ~1/10 của L1)
7. Oracle Problem
7.1 Vấn đề
Smart contract không thể tự access data ngoài blockchain:
- Asset prices (ETH/USD)
- Weather data
- Sports scores
- API responses
Nếu dùng single oracle → centralization, single point of failure
Oracle reports sai giá ETH = $1 → arbitrage drain toàn bộ DeFi protocols
7.2 Chainlink — Decentralized Oracle Network
Multiple independent oracles report data
→ Median / consensus
→ Outliers filtered out
Oracle reputation system:
- Track historical accuracy
- Slash stake nếu report sai
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
contract PriceConsumer {
AggregatorV3Interface internal priceFeed;
constructor() {
// ETH/USD price feed on Ethereum mainnet
priceFeed = AggregatorV3Interface(0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419);
}
function getLatestPrice() public view returns (int) {
(
uint80 roundID,
int price,
uint startedAt,
uint timeStamp,
uint80 answeredInRound
) = priceFeed.latestRoundData();
return price; // 8 decimals
}
}
8. Security & Common Vulnerabilities
8.1 Reentrancy Attack
// Vulnerable contract
contract VulnerableBank {
mapping(address => uint256) public balances;
function withdraw() public {
uint256 amount = balances[msg.sender];
// External call TRƯỚC khi update state → vulnerable!
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
balances[msg.sender] = 0; // ← Too late!
}
}
// Attacker contract
contract Attacker {
VulnerableBank bank;
constructor(address _bank) {
bank = VulnerableBank(_bank);
}
receive() external payable {
// Re-enter withdraw() before balances updated
if (address(bank).balance >= 1 ether) {
bank.withdraw();
}
}
function attack() public payable {
bank.deposit{value: 1 ether}();
bank.withdraw(); // Drain all funds
}
}
Fix: Checks-Effects-Interactions pattern
function withdraw() public {
uint256 amount = balances[msg.sender];
// Update state TRƯỚC external call
balances[msg.sender] = 0;
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
}
8.2 Integer Overflow (trước Solidity 0.8)
// Solidity < 0.8: no overflow protection
uint8 public count = 255;
function increment() public {
count++; // 255 + 1 = 0 (overflow!)
}
// Fix: Use SafeMath hoặc Solidity 0.8+ (built-in overflow check)
8.3 Front-running
MEV (Maximal Extractable Value):
User submit transaction: Buy 100 ETH at $2000
→ Bot sees tx in mempool (before mined)
→ Bot submit tx với gas price cao hơn: Buy 100 ETH first
→ Bot's tx executes first (price increases)
→ User's tx executes at higher price
→ Bot sells immediately (profit)
This is legal but unfair. Solutions:
- Private mempools (Flashbots)
- Commit-reveal schemes
- Batch auctions
9. Blockchain Trilemma
Scalability
/\
/ \
/ \
/ \
/ \
/ \
Decentralization -------- Security
Pick 2:
- Bitcoin: Decentralization + Security, poor Scalability (7 TPS)
- Solana: Scalability + Security, compromised Decentralization (validator hardware expensive)
- Consortium blockchain (Hyperledger): Scalability + Security, no Decentralization
Không có giải pháp hoàn hảo. Mỗi blockchain trade off khác nhau.
10. When NOT to Use Blockchain
10.1 Red Flags
❌ Dùng blockchain khi:
- Cần high throughput (>10k TPS) với low latency (<100ms)
- Có trusted central party (e.g., company internal system)
- Data privacy critical (blockchain = public by default)
- Mutable data (blockchain = immutable)
- Free transactions (gas fees expensive)
✅ Dùng blockchain khi:
- Multi-party collaboration, không ai trust ai
- Cần tamper-proof audit trail
- Asset ownership transfer without intermediary
- Censorship resistance
10.2 Alternatives
Cần immutable audit log?
→ EventStore, Apache Kafka (log compaction disabled)
Cần distributed consensus?
→ Raft, Paxos (faster, no mining)
Cần decentralized storage?
→ IPFS, BitTorrent (not blockchain, but P2P)
Cần digital signature?
→ GPG, JWT với PKI
11. Interview Questions
Q: Explain UTXO vs Account model. Trade-offs?
Answer:
- UTXO: Parallel-friendly, privacy-preserving, stateless verification
- Account: Simple for smart contracts, easier balance tracking
- Bitcoin dùng UTXO vì payment focus, Ethereum dùng Account vì smart contract complexity
Q: Tại sao Ethereum gas fee cao trong bull market?
Answer:
- Block gas limit cố định (~15M gas/block)
- Demand tăng → users bid gas price cao hơn để priority
- Solution: Layer 2 rollups, EIP-4844 (proto-danksharding)
Q: Làm sao prevent reentrancy attack?
Answer:
- Checks-Effects-Interactions pattern
- ReentrancyGuard modifier (OpenZeppelin)
- Pull payment pattern thay vì push
Q: 51% attack có khả thi không?
Answer:
- Bitcoin: Cần >50% hash power (~$10B+ hardware + electricity) → không economic
- Smaller chains: Đã có 51% attacks (Ethereum Classic 2020, Bitcoin Gold 2018)
- PoS: Cần >50% stake + risk slashing → expensive
Tóm tắt
Blockchain không phải silver bullet. Key takeaways:
- Immutability: Không thể sửa history → audit trail, nhưng cũng không thể fix bug
- Decentralization: No single point of failure, nhưng slow consensus
- Trustlessness: Code = law, nhưng code vulnerabilities = exploits
- Trilemma: Scale/Decentralization/Security — pick 2
Khi làm việc với blockchain:
- Hiểu trade-offs (không phải mọi problem cần blockchain)
- Security first (smart contract bugs = millions $ lost)
- Gas optimization matters (SSTORE cost 20k gas)
- Test extensively (mainnet = production with real money)
Tài liệu tham khảo
- Bitcoin Whitepaper — Satoshi Nakamoto
- Ethereum Yellowpaper — Technical spec
- Mastering Bitcoin — Andreas Antonopoulos
- Smart Contract Best Practices — ConsenSys
- DeFi Developer Roadmap