Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
1 change: 1 addition & 0 deletions lib/openzeppelin-contracts
Submodule openzeppelin-contracts added at b65975
307 changes: 307 additions & 0 deletions week-02/quiz/quiz-02-solution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
# Week 2 퀴즈: Transaction/서명 + Foundry

**제출 방법:**
1. 이 파일을 복사하여 `quiz-02-solution.md`로 저장
2. 각 문제에 답변 작성 (왜 그런지 설명 포함)
3. Pull Request 생성 (`quiz_submission` 템플릿 사용)

**평가 기준:**
- 정답 여부보다 **개념 이해도**를 중점 평가합니다
- "왜"에 대한 설명이 충분한지 확인합니다
- 코드 문제는 문법보다 논리적 정확성을 평가합니다

---

## 문제 1: [이론] 트랜잭션 필드 (객관식)

다음 중 이더리움 트랜잭션에서 `gasPrice`와 `gasLimit`의 관계를 올바르게 설명한 것은?

**보기:**
A) gasPrice는 최대 사용량, gasLimit은 단위당 가격이다
B) gasPrice는 단위당 가격, gasLimit은 최대 사용량이다
C) 둘 다 같은 의미이며 호환되어 사용된다
D) gasLimit이 높을수록 트랜잭션이 빨리 처리된다

**답변:**
<!--
정답과 함께, "총 가스 비용"을 계산하는 공식을 설명하세요.
예: 어떤 값들을 곱하면 실제 비용이 나오나요?
-->B. GasPrice란 가스 한단위당 지불할 수 있는 비용, gasLimit이란 사용할 최대 가스량을 의미한다. 즉 총 가스 비용은
gasPrice x gasLimit이다.


---

## 문제 2: [이론] nonce의 역할 (객관식)

다음 상황에서 어떤 일이 발생하나요?

```
Alice가 다음 두 트랜잭션을 동시에 네트워크에 브로드캐스트합니다:
- TX-A: nonce=5, Bob에게 1 ETH (gasPrice: 50 Gwei)
- TX-B: nonce=6, Charlie에게 2 ETH (gasPrice: 100 Gwei)

Alice의 현재 nonce: 5
```

**보기:**
A) TX-B가 gasPrice가 높아서 먼저 처리되고, TX-A는 나중에 처리된다
B) TX-A가 먼저 처리되어야 TX-B가 처리될 수 있다. gasPrice와 무관하게 순서대로 처리된다
C) 두 트랜잭션이 동시에 처리된다
D) 둘 다 실패하고 Alice의 계정이 잠긴다

**답변:**
<!--
정답과 함께, 왜 nonce 순서가 gasPrice보다 우선하는지 설명하세요.
-->B. nonce가 트랜잭션의 순서를 강제하므로 gasPrice가 더 높더라도 TX-A가 먼저 처리된다.


---

## 문제 3: [이론] 디지털 서명 (객관식)

디지털 서명(ECDSA)이 보장하는 세 가지 속성 중, "누군가 내 트랜잭션을 위조할 수 없다"를 보장하는 것은?

**보기:**
A) 인증 (Authentication)
B) 무결성 (Integrity)
C) 부인 방지 (Non-repudiation)
D) 암호화 (Encryption)

**답변:**
<!--
정답과 함께, 나머지 두 속성(인증, 무결성, 부인 방지 중)이
각각 무엇을 보장하는지 간단히 설명하세요.
-->B. 인증은 내가 개인키로 서명했음을 보장하는 것이며 부인 방지는 내가 보내지 않았다는 부인을 방지하며 무결성은 내용이
바뀌지 않았음을 보장하는 것이다.


---

## 문제 4: [이론] 키 유도 (객관식)

다음 중 키 유도 과정에서 올바른 방향을 설명한 것은?

**보기:**
A) Public Key -> Private Key -> Address 순으로 유도된다
B) Address -> Public Key -> Private Key 순으로 역추적 가능하다
C) Private Key -> Public Key -> Address 순으로 유도되며, 역방향은 불가능하다
D) 세 값은 독립적으로 생성되며 서로 연관이 없다

**답변:**
<!--
정답과 함께, "왜 역방향이 불가능한지" 간단히 설명하세요.
힌트: 수학적 난이도 관점에서 생각해 보세요.
-->C. 조사를 해보니 타원곡선 고정 생성점 G를 개인 키 k번 만큼 더한 값이 공개 키이다. 이때 타원 곡선은 비선형적이기에
합을 할때마다 랜덤처럼 튀고 이 때문에 역연산이 거의 불가능에 가깝다. 이를 통해 공개키를 생성한 후 해쉬 함수에 넣어
뒤쪽 20바이트만큼이 주소이다. 그렇기에 개인키 > 공개키 > 주소 순으로 유도 되며 역방향이 불가능하다.


---

## 문제 5: [이론] nonce의 필요성 (단답형)

이더리움에서 **왜** nonce가 필요한가요?

만약 nonce가 없다면 어떤 공격이 가능해질까요? 구체적인 예시와 함께 설명하세요.

**답변:**
<!--
2-3 문장으로 설명하세요.
힌트: "재사용 공격"이란 무엇일까요?
누군가가 당신의 서명된 트랜잭션을 복사해서 여러 번 보내면?
-->재사용 공격이란 동일한 트랜잭션을 공격자가 훔쳐 반복적으로 트랜잭션을 사용하는 공격이다. 예를 들어 내가 누군가에게
보내는 트랜잭션을 누군가 복사하여 계속해서 실행시키는 것이 재사용 공격이다. 이때 nonce는 계정이 트랜잭션을 보낸
횟수로 이를 통해 복제된 트랜잭션을 보내더라도 이미 nonce는 증가하였기에 재사용 공격을 할 수 없게 된다.


---

## 문제 6: [이론] Private Key 보안 (단답형)

2022년 Ronin Bridge 해킹에서 약 $625M이 탈취되었습니다.

**왜** Private Key 유출이 이렇게 치명적인가요? 은행 계좌 비밀번호 유출과 비교해서 설명하세요.

**답변:**
<!--
2-3 문장으로 설명하세요.
힌트: 은행은 이상 거래 취소가 가능합니다. 블록체인은?
-->개인 키 유출이 치명적인 이유는 은행은 중앙 관리자이기에 계좌 동결, 거래 취소가 가능하나 블록체인은 중앙 관리자가
존재하지 않는다. 그렇기에 개인키의 탈취자는 계정의 합법적 소유자이며 이때 생성한 트랜잭션은 되돌릴 수 없기에
개인키 유출이 치명적이다.


---

## 문제 7: [이론] EIP-1559 이해 (단답형)

EIP-1559 이전과 이후의 가스 수수료 메커니즘의 가장 큰 차이점은 무엇인가요?

**힌트:** `baseFee`와 `priorityFee`의 역할을 설명하면서 답변하세요.

**답변:**
<!--
2-3 문장으로 설명하세요.
이전: 가스 가격을 사용자가 직접 설정
이후: 어떤 부분이 달라졌나요?
-->이전에는 가스 가격을 사용자가 직접 설정하여 높은 가스 가격 순으로 블록에 넣어졌기에 실제 필요한 가격보다 높은
지출이 이루어졌으나 이후에는 총 수수료를 baseFee와 priorityFee로 나누어졌다. baseFee는 기본 수수료로 네트워크의
혼잡도에 따라 자동으로 정해진다. priorityFee는 블록 생성자에 대한 팁이다. 이를 통해 과도한 가스 수수료를 줄일 수
있었으며 baseFee는 소각하여 이더리움 가격을 방어할 수 있다.


---

## 문제 8: [코드] SimpleStorage 테스트 (빈칸 채우기)

다음 테스트 코드의 빈칸을 채워서 deposit 기능을 테스트하세요:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import "forge-std/Test.sol";
import "../src/SimpleStorage.sol";

contract SimpleStorageTest is Test {
SimpleStorage public storage_;
address public user = address(0x1);

function setUp() public {
storage_ = new SimpleStorage();
// user에게 10 ETH 부여
vm.deal(user, 10 ether);
}

function test_DepositUpdatesBalance() public {
// Arrange: user 관점에서 실행
vm.prank(use); // TODO: user로 전환하는 코드

// Act: 1 ETH 입금
storage_.deposit{value : 1 ether}(); // TODO: 1 ether를 입금하는 코드

// Assert: 잔액 확인
assertEq(storage_.getBalance(user), 1 ether); // TODO: 예상 잔액
}
}
```

**답변:**
```solidity
// 빈칸을 채운 완성 코드를 작성하세요
```

**왜 이렇게 작성했나요:**
<!--
각 빈칸에 대해 왜 그 코드를 선택했는지 설명하세요.
특히 vm.prank와 {value: ...} 구문의 의미를 설명해 주세요.
-->우선 호출의 msg.sender를 user로 위장하기 위해 vm.prank(user)을 사용해주었다. 그다음 1 ether를 입금하기 위해
SimpleStorage.sol에 있는 함수 deposit()을 사용해야 한다. 그렇기에 storage_.deposit{value : 1 ether}()을 사용해 user가
deposit()을 호출하며 1 ether를 보내게 된 것 이다.(이때 vm.deal(user, 10 ether);을 통해 user에게 임의적으로 10 이더를
주었기에 가능하다.) 그리고 assertEq(storage_.getBalance(user), 1 ether)를 통해 storage_에 있는 user의 잔액을 확인하는
getBalance()를 호출하고 이 잔액이 1 ether인지 assertEq()를 통해 확인 할 수 있다.


---

## 문제 9: [코드] require 조건 (취약점 찾기)

다음 코드에서 잠재적 문제점을 찾으세요:

```solidity
// BAD CODE - 문제점 찾기
contract Wallet {
mapping(address => uint256) public balances;

function deposit() public payable {
balances[msg.sender] += msg.value;
}

function withdraw(uint256 amount) public {
// 잔액 차감
balances[msg.sender] -= amount;

// ETH 전송
payable(msg.sender).transfer(amount);
}
}
```

**1) 발견한 문제점:**
<!--
어떤 검증이 빠져 있나요?
-->withdraw()에 require구문이 빠져있어 Wallet에 빠져나가야 하는 금액 이상이 있는지 확인을 할 수 어뵤다


**2) 왜 이것이 문제인가:**
<!--
이 상태로 withdraw(100 ether)를 호출하면 어떻게 될까요?
힌트: 잔액이 1 ether밖에 없는 경우를 생각해 보세요.
-->1 - 100을 해야하는 데 음수를 기록할 수 없기에 언더플로우가 발생한다.


**3) 올바른 수정 방법:**
```solidity
// GOOD CODE - 수정된 withdraw 함수를 작성하세요
```require(balances[msg.sender] >= amount, "Insufficient balance");

---

## 문제 10: [코드] 테스트 실패 이유 (코드 분석)

다음 테스트가 실패하는 이유를 분석하세요:

```solidity
contract SimpleStorageTest is Test {
SimpleStorage public storage_;

function setUp() public {
storage_ = new SimpleStorage();
}

function test_WithdrawFails() public {
// 입금 없이 바로 출금 시도
vm.expectRevert();
storage_.withdraw(1 ether);
}
}
```

**질문 1:** 이 테스트가 실패하는 이유는 무엇인가요?

**답변:**
<!--
왜 실패할까요? 어떤 require 조건에 걸릴까요?
-->withdraw의 require는 잔액이 출금 금액 이상을 요구하는데 위 테스트는 입금 없이 출금을 시도하였기에 require 구문에
막혀 실패한다.


**질문 2:** 이 테스트를 "출금 실패를 테스트하는 정상 테스트"로 바꾸려면 어떻게 수정해야 하나요?

**답변:**
```solidity
// 힌트: vm.expectRevert를 사용하세요
// 수정된 테스트 코드를 작성하세요
```

---

## 자기 평가

모든 문제를 풀었다면, 아래 체크리스트로 자기 평가를 해보세요:

- [ ] 트랜잭션 필드(nonce, gasPrice, gasLimit 등)의 역할을 이해했다
- [ ] 디지털 서명의 세 가지 보장(인증, 무결성, 부인 방지)을 설명할 수 있다
- [ ] Private Key 보안의 중요성을 이해했다
- [ ] Foundry 테스트 기본 패턴(vm.prank, vm.deal, assertEq)을 사용할 수 있다
- [ ] require 조건의 필요성을 이해했다

---

## 참고 자료

- 이론: `eth-materials/week-02/theory/slides.md`
- 코드: `eth-homework/week-02/dev/src/SimpleStorage.sol`
- 테스트: `eth-homework/week-02/dev/test/SimpleStorage.t.sol`
- 용어: `eth-materials/resources/glossary.md`
15 changes: 15 additions & 0 deletions week-04/dev/my-dapp/.next/app-build-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"pages": {
"/page": [
"static/chunks/webpack.js",
"static/chunks/main-app.js",
"static/chunks/app/page.js"
],
"/layout": [
"static/chunks/webpack.js",
"static/chunks/main-app.js",
"static/css/app/layout.css",
"static/chunks/app/layout.js"
]
}
}
19 changes: 19 additions & 0 deletions week-04/dev/my-dapp/.next/build-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"polyfillFiles": [
"static/chunks/polyfills.js"
],
"devFiles": [],
"ampDevFiles": [],
"lowPriorityFiles": [
"static/development/_buildManifest.js",
"static/development/_ssgManifest.js"
],
"rootMainFiles": [
"static/chunks/webpack.js",
"static/chunks/main-app.js"
],
"pages": {
"/_app": []
},
"ampFirstPages": []
}
1 change: 1 addition & 0 deletions week-04/dev/my-dapp/.next/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"type": "commonjs"}
Loading