Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
388 changes: 388 additions & 0 deletions week-04/quiz/quiz-04-solution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,388 @@
# Week 4 Quiz: Network/Block + wagmi

> **제출 방법:** 이 파일을 복사하여 답변을 작성한 후, PR로 제출하세요.
> **평가 기준:** 개념 이해도 중심 - 문법 오류보다 논리적 설명을 중시합니다.

---

## 문제 1: 블록 헤더 필드 (객관식)

다음 상황을 고려하세요:

```
블록 100의 해시: 0xabc123...
블록 101의 해시: 0xdef456...
```

블록 101의 `parentHash` 필드에는 어떤 값이 저장되어 있나요? 그리고 **왜** 이런 방식으로 연결하나요?

**보기:**
A) 0xdef456... - 자기 자신의 해시를 저장하여 무결성을 보장한다
B) 0xabc123... - 이전 블록의 해시를 저장하여 체인 연결과 불변성을 보장한다
C) 블록 번호 100 - 숫자로 순서를 추적한다
D) 빈 값 - 헤더에는 해시가 저장되지 않는다

**답변:**
B) 블록 101의 parentHash에는 바로 이전 블록인 블록 100의 해시 `0xabc123...`이 저장됩니다. 이것이 "블록체인"이라 불리는 이유입니다 — 각 블록이 이전 블록의 해시를 참조하여 체인처럼 연결됩니다. A는 자기 자신의 해시를 저장하는 것으로, 해시는 블록 내용 전체로부터 계산되므로 자기 참조는 불가능합니다. C는 단순 번호로는 데이터 무결성을 보장할 수 없습니다. D는 사실이 아닙니다. parentHash를 통한 연결은 한 블록만 변조해도 그 뒤의 모든 블록 해시가 불일치하게 만들어, 변조를 즉시 감지할 수 있는 불변성의 핵심 메커니즘입니다.


---

## 문제 2: MPT 목적 (객관식)

이더리움에서 Merkle Patricia Trie(MPT)를 사용하는 **가장 중요한 이유**는 무엇인가요?

**보기:**
A) 데이터를 암호화하여 외부에서 읽을 수 없게 한다
B) 트랜잭션 처리 속도를 10배 이상 높인다
C) 전체 데이터 없이도 특정 데이터의 존재와 정확성을 효율적으로 증명한다
D) 블록 크기를 줄여서 저장 공간을 절약한다

**답변:**
C) MPT의 핵심 가치는 **효율적인 증명(proof)**입니다. Light Node는 블록 헤더의 Root Hash만 가지고 있어도, 전체 상태 데이터(수백 GB)를 다운로드하지 않고 특정 계정의 잔액이나 트랜잭션이 실제로 존재하는지 검증할 수 있습니다. Merkle Proof라는 짧은 경로 데이터만 있으면 Root Hash와 대조하여 데이터의 정확성을 수학적으로 입증할 수 있습니다. 이 덕분에 모바일 지갑이나 저사양 기기에서도 블록체인을 신뢰성 있게 사용할 수 있습니다. A는 틀렸습니다 — 블록체인 데이터는 공개됩니다.


---

## 문제 3: 체인 연결과 보안 (객관식)

공격자가 블록 50의 트랜잭션을 수정하려고 합니다. 현재 체인의 최신 블록은 100입니다. 이 공격이 **왜** 어려운가요?

**보기:**
A) 블록 50은 너무 오래되어서 시스템에서 접근할 수 없다
B) 블록 50을 수정하면 해시가 바뀌고, 블록 51부터 100까지 모든 블록의 parentHash가 불일치하게 된다
C) 블록 50은 이미 암호화되어 있어서 복호화 키가 필요하다
D) 네트워크 관리자만 과거 블록을 수정할 수 있다

**답변:**
B) 블록 50의 내용을 수정하면 블록 50의 해시가 변합니다. 그러면 블록 51의 parentHash가 더 이상 블록 50의 새 해시와 일치하지 않으므로, 블록 51도 수정해야 합니다. 블록 51을 수정하면 블록 51의 해시도 변하므로 블록 52도 수정해야 하고... 이렇게 블록 100까지 총 50개 블록을 모두 다시 계산해야 합니다. PoS에서는 이 모든 블록에 대해 검증자들의 서명을 위조해야 하므로, 전체 스테이킹의 2/3 이상을 장악하지 않는 한 사실상 불가능합니다. 이것이 블록체인의 불변성(immutability)이 작동하는 원리입니다.


---

## 문제 4: MPT 진화 과정 (단답형)

MPT(Merkle Patricia Trie)는 세 가지 자료구조의 장점을 결합한 것입니다:
1. **Trie** -> 2. **Patricia Trie** -> 3. **Merkle Patricia Trie**

**왜** 각 단계의 발전이 필요했나요? 각 단계가 해결하는 문제를 간단히 설명하세요.

**답변:**
1. **Trie가 해결하는 문제:**
키-값 데이터를 효율적으로 검색할 수 있는 트리 구조를 제공합니다. 키의 각 문자(이더리움에서는 hex nibble)가 트리의 한 레벨이 되어, 키를 따라가면 해당 값을 찾을 수 있습니다. 해시 테이블과 달리 순서가 보장되어 같은 데이터 집합은 항상 같은 트리 구조를 만듭니다.

2. **Patricia Trie가 해결하는 문제 (Trie의 한계):**
일반 Trie는 공통 접두사가 긴 키들이 많으면 빈 노드가 과도하게 생겨 공간 낭비가 심합니다. Patricia Trie는 공통 접두사를 하나의 노드로 **압축(path compression)**하여 공간 효율성을 크게 향상시킵니다. 이더리움 주소는 40자리 hex로 매우 길어서 이 압축이 필수적입니다.

3. **Merkle Patricia Trie가 해결하는 문제 (Patricia Trie의 한계):**
Patricia Trie만으로는 데이터의 무결성을 검증할 수 없습니다. Merkle Tree의 해시 연결 방식을 결합하여, 루트 해시(Root Hash) 하나로 전체 트리의 무결성을 증명할 수 있게 됩니다. Light Node가 전체 데이터 없이도 특정 데이터의 존재를 효율적으로 검증(Merkle Proof)할 수 있습니다.


---

## 문제 5: Eclipse Attack 방어 (단답형)

Eclipse Attack은 공격자가 피해자 노드의 **모든 피어 연결**을 자신이 통제하는 노드로 바꾸는 공격입니다.

1) 이 공격이 성공하면 피해자에게 **어떤 피해**가 발생할 수 있나요?
2) 개인 노드 운영자가 이 공격을 **방어**하기 위해 할 수 있는 행동은 무엇인가요?

**답변:**
1) 가능한 피해:
- **이중 지불 공격**: 공격자가 피해자에게 가짜 트랜잭션(이미 다른 곳에서 사용된 ETH)을 보여주고, 피해자는 이를 유효하다고 믿습니다.
- **검열**: 피해자의 트랜잭션을 네트워크에 전파하지 않거나, 특정 트랜잭션을 숨길 수 있습니다.
- **셀피시 마이닝 지원**: 공격자가 만든 블록만 보여주어 피해자의 해시파워를 공격자의 체인에 낭비하게 합니다.
- **잘못된 체인 정보**: 피해자가 실제 정규 체인이 아닌 공격자의 포크를 따르게 됩니다.

2) 방어 방법:
- **신뢰할 수 있는 노드를 수동으로 피어 목록에 추가**: 알려진 부트노드나 신뢰할 수 있는 피어를 고정(static peer)으로 설정합니다.
- **다양한 네트워크 경로 사용**: 여러 ISP나 VPN을 통해 연결하여 단일 경로 차단을 방지합니다.
- **피어 연결 수 모니터링**: 비정상적으로 같은 IP 대역에서 많은 연결이 들어오면 의심합니다.
- **피어 다양성 제한 설정**: 같은 /24 서브넷에서 연결되는 피어 수를 제한하여 하나의 공격자가 모든 슬롯을 차지하지 못하게 합니다.


---

## 문제 6: 노드 종류 선택 (단답형)

친구가 이더리움 개발을 시작하려고 합니다. 다음 세 가지 상황에서 각각 어떤 노드 타입(Full, Light, Archive)을 추천하시겠습니까? **왜** 그 노드를 추천하는지도 설명하세요.

**답변:**
1) 모바일 지갑 앱:
추천 노드: **Light Node**
이유: 모바일 기기는 저장 공간과 대역폭이 제한적입니다. Light Node는 블록 헤더만 저장하므로 몇 MB 수준이며, Merkle Proof를 통해 필요한 데이터를 검증합니다. 실시간 잔액 확인과 트랜잭션 전송에 충분합니다.

2) 블록체인 데이터 분석 서비스:
추천 노드: **Archive Node**
이유: 데이터 분석에는 과거 모든 블록의 모든 상태(특정 블록 높이에서의 잔액, 컨트랙트 상태 등)가 필요합니다. Archive Node는 모든 역사적 상태를 보존하므로 "100번째 블록에서 이 계정의 잔액은?"같은 쿼리가 가능합니다. 수 TB의 저장 공간이 필요하지만 분석에는 필수입니다.

3) 일반적인 dApp 백엔드:
추천 노드: **Full Node**
이유: dApp 백엔드는 현재 상태 조회, 트랜잭션 전송, 이벤트 모니터링 등이 주요 기능입니다. Full Node는 최신 상태와 최근 블록 데이터를 모두 보유하면서도 Archive Node보다 저장 공간이 적습니다(약 500GB~1TB). 과거 모든 상태가 필요 없다면 Full Node가 비용 대비 효율적입니다.


---

## 문제 7: useAccount Hook (빈칸 채우기)

다음 코드의 빈칸을 채워서 지갑 연결 상태를 표시하는 컴포넌트를 완성하세요:

```typescript
import { _________________ } from 'wagmi';

function WalletStatus() {
// TODO: useAccount hook에서 필요한 값들을 가져오세요
const { _________________, _________________ } = useAccount();

if (!isConnected) {
return <div>지갑이 연결되지 않았습니다</div>;
}

return (
<div>
<p>연결된 주소: {address}</p>
</div>
);
}
```

**답변:**
```typescript
import { useAccount } from 'wagmi';

function WalletStatus() {
const { address, isConnected } = useAccount();

if (!isConnected) {
return <div>지갑이 연결되지 않았습니다</div>;
}

return (
<div>
<p>연결된 주소: {address}</p>
</div>
);
}
```

**왜 이렇게 작성했나요:**
`useAccount`는 wagmi에서 현재 연결된 지갑의 정보를 가져오는 Hook입니다. 주요 반환값:
- `address`: 연결된 지갑의 이더리움 주소 (`0x...` 형식). 연결되지 않으면 `undefined`.
- `isConnected`: 지갑 연결 여부를 나타내는 boolean. 연결되면 `true`.
- 그 외: `chain` (연결된 네트워크), `connector` (사용 중인 지갑 커넥터), `status` (연결 상태) 등도 제공합니다.
코드에서는 `isConnected`로 연결 여부를 먼저 확인하고, 연결된 경우에만 `address`를 표시합니다.


---

## 문제 8: useReadContract Hook (빈칸 채우기)

다음 코드의 빈칸을 채워서 컨트랙트의 `getCount` 함수 결과를 화면에 표시하세요:

```typescript
import { useReadContract } from 'wagmi';

const counterABI = [
{
name: 'getCount',
type: 'function',
stateMutability: 'view',
inputs: [],
outputs: [{ name: 'count', type: 'uint256' }],
},
] as const;

function CountDisplay() {
const { data, isLoading, error } = useReadContract({
// TODO: 필요한 설정을 채우세요
address: '0x1234...5678',
_________________,
_________________,
});

if (isLoading) return <div>로딩 중...</div>;
if (error) return <div>에러 발생</div>;

return <div>현재 카운트: {_________________}</div>;
}
```

**답변:**
```typescript
function CountDisplay() {
const { data, isLoading, error } = useReadContract({
address: '0x1234...5678',
abi: counterABI,
functionName: 'getCount',
});

if (isLoading) return <div>로딩 중...</div>;
if (error) return <div>에러 발생</div>;

return <div>현재 카운트: {data?.toString()}</div>;
}
```

**왜 이렇게 작성했나요:**
`useReadContract`는 컨트랙트의 view/pure 함수를 호출하는 Hook입니다. 필수 설정:
- `abi`: 컨트랙트의 ABI (Application Binary Interface). 함수의 이름, 파라미터, 반환 타입 등을 정의합니다. 이것이 있어야 wagmi가 올바른 함수 시그니처를 인코딩할 수 있습니다.
- `functionName`: 호출할 함수 이름. ABI에 정의된 함수명과 일치해야 합니다.

`data`를 표시할 때 `data?.toString()`을 사용하는 이유는, 컨트랙트에서 반환되는 uint256 값이 JavaScript의 `BigInt` 타입이기 때문입니다. BigInt는 직접 JSX에 렌더링할 수 없으므로 문자열로 변환해야 합니다. `?.`는 data가 undefined일 수 있으므로 옵셔널 체이닝을 사용합니다.


---

## 문제 9: useWriteContract 버그 (취약점 찾기)

다음 코드에서 **문제점**을 찾고 수정하세요:

```typescript
// BAD CODE - 문제점 찾기
import { useWriteContract } from 'wagmi';

function IncrementButton() {
const { writeContract, isPending } = useWriteContract();

const handleClick = () => {
// 문제가 있는 코드
writeContract({
address: '0x1234...5678',
functionName: 'increment',
// abi가 없음!
});
};

return (
<button onClick={handleClick} disabled={isPending}>
증가하기
</button>
);
}
```

**1) 발견한 문제점:**
`writeContract` 호출에 `abi`가 빠져 있습니다. ABI는 wagmi에서 컨트랙트 함수를 호출할 때 필수 파라미터입니다.


**2) 왜 이것이 문제인가:**
ABI가 없으면 wagmi는 `increment` 함수의 시그니처(파라미터 타입, 반환 타입 등)를 알 수 없습니다. 함수를 호출하려면 함수 시그니처를 keccak256으로 해시하여 함수 셀렉터(4바이트)를 만들어야 하는데, ABI 없이는 이 과정이 불가능합니다. TypeScript 단계에서 타입 에러가 발생하거나, 런타임에서 인코딩 실패 에러가 발생합니다.


**3) 올바른 수정 방법:**
```typescript
import { useWriteContract } from 'wagmi';

const counterABI = [
{
name: 'increment',
type: 'function',
stateMutability: 'nonpayable',
inputs: [],
outputs: [],
},
] as const;

function IncrementButton() {
const { writeContract, isPending } = useWriteContract();

const handleClick = () => {
writeContract({
address: '0x1234...5678',
abi: counterABI,
functionName: 'increment',
});
};

return (
<button onClick={handleClick} disabled={isPending}>
증가하기
</button>
);
}
```

---

## 문제 10: 블록 연결 구조 (다이어그램 해석)

```mermaid
graph LR
subgraph B0["제네시스 블록"]
H0["hash: 0xabc..."]
end
subgraph B1["블록 1"]
PH1["parent: 0xabc..."]
H1["hash: 0xdef..."]
end
subgraph B2["블록 2"]
PH2["parent: 0xdef..."]
H2["hash: 0x123..."]
end
subgraph B3["블록 3"]
PH3["parent: ???"]
H3["hash: 0x789..."]
end

B0 --> B1 --> B2 --> B3
```

**질문:**

1) 블록 3의 `parent: ???` 에 들어갈 값은 무엇인가요?

**답변:** `0x123...` — 블록 2의 해시입니다. 각 블록의 parentHash는 바로 직전 블록의 해시를 참조합니다.


2) 만약 블록 1의 내용이 수정되면, 블록 2와 블록 3에 **어떤 영향**이 있나요? 왜 그런가요?

**답변:** 블록 1의 내용이 수정되면 블록 1의 해시(0xdef...)가 변합니다. 그러면 블록 2의 parentHash(0xdef...)가 더 이상 블록 1의 새 해시와 일치하지 않아 블록 2가 무효화됩니다. 블록 2를 수정하면 블록 2의 해시(0x123...)도 변하므로 블록 3의 parentHash와 불일치가 발생합니다. 결국 블록 1 이후의 모든 블록을 다시 계산해야 합니다. 이것이 블록체인이 과거 데이터 변조에 강한 이유입니다.


3) 제네시스 블록(블록 0)의 parentHash는 어떤 특별한 값을 가지나요? 왜 그런가요?

**답변:** 제네시스 블록의 parentHash는 `0x0000...0000` (64개의 0, 32바이트 영값)입니다. 제네시스 블록은 체인의 첫 번째 블록이므로 참조할 이전 블록이 존재하지 않습니다. 따라서 규약상 영(zero) 해시를 parentHash로 사용하여 "이전 블록이 없음"을 나타냅니다.


---

## 문제 11: MPT 트리 구조 (다이어그램 해석)

```mermaid
graph TD
ROOT["Root Hash: 0xfff..."] --> EXT1["Extension Node<br/>path: 0a"]
ROOT --> EXT2["Extension Node<br/>path: 0b"]

EXT1 --> BRANCH["Branch Node<br/>(16개 슬롯)"]
BRANCH --> LEAF1["Leaf: 계정 A<br/>주소: 0a1234..."]
BRANCH --> LEAF2["Leaf: 계정 B<br/>주소: 0a5678..."]

EXT2 --> LEAF3["Leaf: 계정 C<br/>주소: 0b9999..."]
```

**질문:**

1) 계정 A와 계정 B가 같은 Branch Node 아래에 있는 이유는 무엇인가요?

**답변:** 계정 A(0a1234...)와 계정 B(0a5678...)는 주소의 앞부분 `0a`가 동일합니다. Extension Node가 공통 접두사 `0a`를 처리하고, 그 이후 분기가 필요한 시점(A는 `1`, B는 `5`)에서 Branch Node가 사용됩니다. Branch Node는 16개의 hex nibble(0-f) 슬롯을 가지며, A는 슬롯 `1`로, B는 슬롯 `5`로 분기합니다.


2) Extension Node가 하는 역할은 무엇인가요? 없다면 어떤 문제가 생기나요?

**답변:** Extension Node는 여러 키가 공유하는 공통 경로를 하나의 노드로 압축(path compression)합니다. Extension Node가 없다면, `0a`라는 2자리 경로를 표현하기 위해 2개의 Branch Node(첫째는 `0`, 둘째는 `a`)가 필요합니다. 이더리움 주소는 40자리 hex이므로 공통 접두사가 길 경우 수십 개의 불필요한 Branch Node가 생겨 트리의 깊이와 저장 공간이 크게 낭비됩니다. Extension Node 덕분에 트리가 훨씬 효율적으로 됩니다.


3) Root Hash만 알면 어떻게 특정 계정의 데이터 존재를 **증명**할 수 있나요?

**답변:** Light Client는 Full Node에게 특정 계정에 대한 **Merkle Proof**를 요청합니다. 이 Proof는 Root에서 해당 Leaf까지의 경로상 모든 노드의 해시값입니다. 예를 들어 계정 A를 증명하려면: Root Hash → Extension Node(0a) 해시 → Branch Node 해시 → Leaf A 해시 경로를 받습니다. Light Client는 Leaf A의 데이터를 해시하고, 경로를 따라 올라가면서 각 해시를 계산하여 최종적으로 Root Hash와 일치하는지 확인합니다. 일치하면 해당 데이터가 전체 상태 트리에 실제로 포함되어 있음이 수학적으로 증명됩니다. 전체 상태 데이터 없이 경로 데이터(수 KB)만으로 검증이 가능합니다.


---

## 제출 전 체크리스트

- [x] 모든 문제에 답변을 작성했는가?
- [x] 객관식 문제: 정답 선택 **이유**를 설명했는가?
- [x] 단답형 문제: 2-3문장 이상으로 충분히 설명했는가?
- [x] 코드 문제: 완성된 코드와 **왜 그렇게 작성했는지** 설명했는가?
- [x] 다이어그램 문제: 각 질문에 논리적으로 답변했는가?