From 6568919151af93f9cfb46f08eb4dcdf284009043 Mon Sep 17 00:00:00 2001 From: seokhoon Date: Tue, 10 Feb 2026 19:13:44 +0900 Subject: [PATCH] feat(week-01): complete counter assignment --- week-01/dev/README.md | 2 +- week-01/dev/src/Counter.sol | 10 +- week-01/dev/test/Counter.t.sol | 2 +- week-01/quiz/quiz-01-solution.md | 293 +++++++++++++++++++++++++++++ week-01/theory/quiz-01-solution.md | 71 +++++++ 5 files changed, 370 insertions(+), 8 deletions(-) create mode 100644 week-01/quiz/quiz-01-solution.md create mode 100644 week-01/theory/quiz-01-solution.md diff --git a/week-01/dev/README.md b/week-01/dev/README.md index 6f6f80ae..a08cb92d 100644 --- a/week-01/dev/README.md +++ b/week-01/dev/README.md @@ -20,7 +20,7 @@ ``` week-01/dev/ ├── src/ -│ └── Counter.sol # 과제 파일 (TODO를 구현하세요) +│ └── Counter.sol # 과제 파일 (TODO를 구현하세요) ├── test/ │ └── Counter.t.sol # 테스트 파일 (수정하지 마세요) ├── script/ diff --git a/week-01/dev/src/Counter.sol b/week-01/dev/src/Counter.sol index f9687e3a..042c2d33 100644 --- a/week-01/dev/src/Counter.sol +++ b/week-01/dev/src/Counter.sol @@ -30,22 +30,20 @@ contract Counter { /// @notice 카운트를 1 증가시킵니다 /// @dev count 값을 1만큼 증가시키는 로직을 구현하세요 function increment() public { - // TODO: count를 1 증가시키세요 + count++; // 힌트: count += 1; 또는 count = count + 1; 또는 count++; } /// @notice 카운트를 1 감소시킵니다 /// @dev count가 0일 때 감소시키면 언더플로우가 발생합니다 function decrement() public { - // TODO: count를 1 감소시키세요. 단, count가 0이면 revert해야 합니다. - // 힌트: require(조건, "에러 메시지"); 를 사용하세요 - // 힌트: require(count > 0, "Count cannot go below zero"); + require(count > 0, "Count cannot go below zero"); + count--; } /// @notice 카운트를 0으로 초기화합니다 /// @dev count 값을 0으로 설정하는 로직을 구현하세요 function reset() public { - // TODO: count를 0으로 초기화하세요 - // 힌트: count = 0; + count = 0; } } diff --git a/week-01/dev/test/Counter.t.sol b/week-01/dev/test/Counter.t.sol index 3f9c482c..2a882594 100644 --- a/week-01/dev/test/Counter.t.sol +++ b/week-01/dev/test/Counter.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.26; -import "forge-std/Test.sol"; +import {Test} from "forge-std/Test.sol"; import "../src/Counter.sol"; /// @title CounterTest - Week 1 테스트 diff --git a/week-01/quiz/quiz-01-solution.md b/week-01/quiz/quiz-01-solution.md new file mode 100644 index 00000000..9166b1f6 --- /dev/null +++ b/week-01/quiz/quiz-01-solution.md @@ -0,0 +1,293 @@ +# Week 1 퀴즈: State/Account + Solidity 기초 + +**제출 방법:** +1. 이 파일을 복사하여 `quiz-01-solution.md`로 저장 +2. 각 문제에 답변 작성 (왜 그런지 설명 포함) +3. Pull Request 생성 (`quiz_submission` 템플릿 사용) + +**평가 기준:** +- 정답 여부보다 **개념 이해도**를 중점 평가합니다 +- "왜"에 대한 설명이 충분한지 확인합니다 +- 문법 오류는 크게 감점하지 않습니다 + +--- + +## 문제 1: [이론] 상태 머신 (객관식) + +이더리움에서 "상태 전이가 원자적(atomic)이다"라는 말의 의미를 가장 잘 설명한 것은? + +다음 상황을 고려하세요: + +``` +Alice가 Bob에게 1 ETH를 보내는 트랜잭션을 실행합니다. +중간에 가스가 부족해져서 트랜잭션이 실패했습니다. +``` + +**보기:** +A) Alice의 잔액만 감소하고 Bob의 잔액은 변하지 않는다 +# B) Alice의 잔액과 Bob의 잔액 모두 변하지 않고, 가스비만 소모된다 +C) 네트워크가 자동으로 부족한 가스를 보충해서 트랜잭션을 완료한다 +D) 트랜잭션이 절반만 실행되어 0.5 ETH만 전송된다 + +**답변:** + +B. 이유는 atomic 특성상 transaction 과정에서 단계가 하나라도 성립하지 않는다면, 그 transaction은 유효하지 않은 것이 된다. +그렇기 때문에 transaction은 취소되며, gas비는 몰수된다. +몰수되는 이유는 validator가 transaction을 검증해야 하므로 이 과정에서 발생하는 임금?의 개념으로 지불해야되기 때문이다. + +'원자적'이라는 의미는 single, indivisible unit으로 여겨진다는 보장이다. 이 특성을 통해서 결과는 Full execution / the revert 이 두 상황에 대한 값만 가지게 된다. (부분적 실행은 불가능하다는 의미이다.) +--- + +## 문제 2: [이론] 결정론적 실행 (객관식) + +이더리움 EVM이 "결정론적(deterministic)"으로 실행된다는 것의 핵심 이유는 무엇인가요? + +**보기:** +A) 모든 노드가 같은 하드웨어를 사용해야 해서 +# B) 같은 입력(트랜잭션)이 주어지면 모든 노드가 같은 결과(상태)를 도출해야 하므로 +C) 중앙 서버가 모든 계산을 수행하고 결과를 배포해서 +D) 트랜잭션이 항상 1초 안에 처리되어야 해서 + +**답변:** + +B +만약, 결정론적이 않으면 이러한 문제가 발생한다. +합의 붕괴 - 부동소수점 연산처럼 환경에 따라 다른 값이 나오는 경우, 어떤 노드는 블록을 유효하다고 판단하고 어떤 노드는 무효라고 판단할 수 있다. +race condition & MEV 문제 - 순서에 따라서 transaction의 영향을 받기 쉬운 환경인데, 이러한 순서를 무시할 수도 있는 취약성이 발생한다. +smart contract trust 붕괴 - 같은 함수를 호출하더라도 같은 결과가 발생하지 않을 수 있다. 그렇게 된다면 노드A와 노드B가 다른 결과를 얻게될 수 있는 환경이 주어진다. +--- + +## 문제 3: [이론] EOA vs CA (객관식) + +다음 중 EOA(Externally Owned Account)와 CA(Contract Account)의 차이를 올바르게 설명한 것은? + +**보기:** +A) EOA는 코드를 실행할 수 있고, CA는 코드를 실행할 수 없다 +# B) EOA만 트랜잭션을 시작할 수 있고, CA는 EOA에 의해 호출될 때만 실행된다 +C) CA만 ETH를 보유할 수 있고, EOA는 ETH를 보유할 수 없다 +D) EOA와 CA는 동일한 기능을 가지며 이름만 다르다 + +**답변:** + +B +EOA : 사람이 소유하는 계정 - private key ✅ / transaction sign ✅ +CA : 스마트 컨트랙트 계정 - private key ❌ / transaction sign ❌ +transaction을 시작하려면 private key가 존재해야 하는데, 이 때 CA는 private key가 존재하지 않기에 실행할 수 없다. 그렇기에 EOA의 호출에 의해 CA가 실행된다. +--- + +## 문제 4: [이론] 계정 상태 필드 (객관식) + +이더리움 계정 상태의 4가지 필드 중 `nonce`의 역할을 올바르게 설명한 것은? + +다음 상황을 고려하세요: + +``` +Alice의 현재 nonce: 5 +Alice가 두 개의 트랜잭션을 동시에 전송합니다: +- TX-A: nonce=5, Bob에게 1 ETH 전송 +- TX-B: nonce=5, Charlie에게 2 ETH 전송 +``` + +**보기:** +A) 두 트랜잭션 모두 성공적으로 처리된다 +# B) TX-A만 처리되고 TX-B는 무시된다 (또는 그 반대) +C) 두 트랜잭션 모두 실패하고 Alice의 자산이 동결된다 +D) 네트워크가 자동으로 TX-B의 nonce를 6으로 변경한다 + +**답변:** + +B +nonce : number used once의 말로 n번째 transaction을 작성하는 번호표의 의미임. +그래서 transaction이 성사될 때 nonce라는 번호표가 같이 가게 되는거고, 이 때 같은 번호표를 가지고 추가적인 거래를 제제함으로써 코인의 복제를 제제하는 것이다. + +--- + +## 문제 5: [이론] World State (객관식) + +World State에 대한 설명 중 올바른 것은? + +**보기:** +A) World State는 최신 100개 블록의 트랜잭션만 저장한다 +# B) World State는 모든 계정의 현재 상태(주소 -> 상태 매핑)를 나타낸다 +C) World State는 EOA의 정보만 저장하고 CA 정보는 별도로 관리한다 +D) World State는 각 노드마다 다른 값을 가질 수 있다 + +**답변:** + +1. 전화번호부와 World State는 key-value 쌍으로 저장되는 부분이 비슷하다. World State같은 경우 key : Account address, value : Account state로 볼 수 있다. +2. 전화번호가 바뀌게 된다면 내용을 덮어쓴다. World State 또한 이전의 내역을 저장하는 것이 아니고 현재의 값을 저장하기 때문에 유사한 점이 있다. +3. 전화번호를 저장할 때 사용되는 메모, 직업 등의 역할은 World State 안에 CA는 추가정보인 Code Hash, Storage Root의 정보가 존재하기 때문이다. +--- + +## 문제 6: [이론] 상태 변수 vs 지역 변수 (단답형) + +Solidity에서 `상태 변수(state variable)`와 `지역 변수(local variable)`의 차이는 무엇인가요? + +다음 코드를 보고 설명하세요: + +```solidity +contract Example { + uint256 public count; // 이것은 무엇인가요? + + function calculate(uint256 input) public pure returns (uint256) { + uint256 result = input * 2; // 이것은 무엇인가요? + return result; + } +} +``` + +**답변:** + +count : state variable - 1. Storage 에 저장된다. 2. 컨트랙트가 존재하는 동안 3. 매우 비쌈(모든 이더리움 노드가 이 값을 저장해야 하기 때문에 비용이 비싸다) +result : local variable - 1. Memory에 저장된다. 2. 함수 실행하는 동안만 3. 쌈 +--- + +## 문제 7: [이론] 원자성의 이유 (단답형) + +이더리움에서 트랜잭션이 "원자적(atomic)"으로 처리되어야 하는 이유는 무엇인가요? + +**왜** 부분적으로 성공하는 트랜잭션을 허용하면 문제가 될까요? 구체적인 예시와 함께 설명하세요. + +**답변:** + +가장 근본적인 문제는 State inconsistency이다. 이 때 half-finished와 같은 절반만 성공하는 거래가 나올 수 있다. 이 때 내가 1ETH를 보내는 대가로 3000USDT를 받는다고 가정돼 있다면, 이 거래에서 나는 1ETH를 전송했지만, 3000USDT는 받지 못한 상태의 half-finished상태가 발생할 수 있다. 이러한 경우를 방지하는 것은 atomic을 통해서 완전한 성공 또는 완전한 실패로 transaction을 처리하는 것이다. + +--- + +## 문제 8: [이론] 계정 구조 설명 (단답형) + +EOA에는 `codeHash`와 `storageRoot`가 왜 의미가 없나요? + +**답변:** + +EOA : 사람의 고유키를 갖고 있는 계정. +CA : 네트워크에 배포하는 고유키가 없는 계정. + +EOA의 계정에는 잔고(balance)와 nonce만이 존재하므로 코드가 존재하지 않는다. ++ EOA에도 codeHash, storageRoot 필드는 존재함. 하지만, 실제 코드와 저장소가 없어서 비어 있는 값임. +--- + +## 문제 9: [코드] Counter 읽기 (코드 읽기) + +다음 Counter.sol 코드를 분석하세요: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity 0.8.26; + +contract Counter { + uint256 public count; + + function getCount() public view returns (uint256) { + return count; + } + + function increment() public { + count += 1; + } + + function decrement() public { + require(count > 0, "Count cannot go below zero"); + count -= 1; + } +} +``` + +**1) `public` 키워드의 역할:** +`count` 변수에 `public`이 붙으면 어떤 일이 자동으로 일어나나요? + +**답변:** + +읽기 전용 함수인 getter 함수를 생성한다. 이 함수는 변수와 같은 이름을 갖고, 외부에서 값을 조회할 수 있다. + + +**2) `view` 키워드의 의미:** +`getCount()` 함수에 `view`가 붙은 이유는 무엇인가요? `view`를 제거하면 어떻게 될까요? + +**답변:** + +view 함수는 블록체인의 상태를 읽을 수는 있지만, 상태를 변경할 수는 없도록 하기 위해서이다. +제거하게 된다면 결괏값에서는 이상은 없다. 하지만, 상태를 변경할 수 있게 되고 이를 통해 gas가 소비되는 상황이 발생한다. 이 이유는 view함수가 있다면 단순히 노드에 물어보는 eth_call형식으로 gas가 소비되지 않는 반면에 view함수가 없다면 transaction으로 실행되어 gas를 소비한다. +--- + +## 문제 10: [코드] Counter 동작 예측 (코드 읽기) + +위의 Counter 컨트랙트에서 다음 시나리오를 분석하세요: + +**시나리오:** +``` +초기 상태: count = 0 + +1. increment() 호출 +2. increment() 호출 +3. decrement() 호출 +4. decrement() 호출 +5. decrement() 호출 +``` + +**질문 1:** 5번째 `decrement()` 호출의 결과는 무엇인가요? + +**답변:** + +실패하게 된다. Ethereum의 atomicity의 특성에 의하면 성공 혹은 실패의 상황만 나온다. 그렇기에 5번 째의 decrease를 실행하게 된다면 uint256 자료형의 특성상 underflow가 발생한다. + +0.8.26버전에서는 require를 작성하지 않아도 자동적으로 음수가 되어 underflow가 되는 현상을 예방한다고 한다. 하지만, 그렇지 않다면 2^256 -1의 수가 출력되게 된다. + +**질문 2:** 왜 `decrement()` 함수에 `require(count > 0, ...)` 조건이 필요한가요? + +**답변:** + +0.8.26버전에서는 require를 작성하지 않아도 자동적으로 음수가 되어 underflow가 되는 현상을 예방한다고 한다. 하지만, 그렇지 않다면 2^256 -1의 수가 출력되게 된다. +--- + +## 자기 평가 + +모든 문제를 풀었다면, 아래 체크리스트로 자기 평가를 해보세요: + +- [x] 상태 머신과 원자성 개념을 이해했다 +- [x] EOA와 CA의 차이를 설명할 수 있다 +- [x] 계정 상태의 4가지 필드(nonce, balance, storageRoot, codeHash)를 이해했다 +- [x] Solidity 기본 문법(public, view, require)을 이해했다 +- [x] 상태 변수와 지역 변수의 차이를 설명할 수 있다 + +--- + +## 참고 자료 + +- 이론: `eth-materials/week-01/theory/slides.md` +- 코드: `eth-homework/week-01/dev/src/Counter.sol` +- 용어: `eth-materials/resources/glossary.md` diff --git a/week-01/theory/quiz-01-solution.md b/week-01/theory/quiz-01-solution.md new file mode 100644 index 00000000..7e0451ac --- /dev/null +++ b/week-01/theory/quiz-01-solution.md @@ -0,0 +1,71 @@ +# Week 1 이론 퀴즈 + +이 퀴즈를 복사하여 `quiz-01-solution.md`로 저장한 후 답변을 작성하세요. + +--- + +## 문제 1: 블록체인 기초 + +블록체인의 핵심 목적은 무엇인가요? 중앙화된 데이터베이스와 비교하여 설명해주세요. + +**답변:** +[여기에 답변을 작성하세요] +중앙화 시스템과 달리 블록체인은 탈중앙화 시스템을 추구한다. 이는 합의 매커니즘을 통해서 인간 조직의 허락을 맡는 것이 아닌 코드를 믿는 것이다. +또한, 중앙화된 정보는 현재의 정보만을 기록하는 것과 달리, 탈중앙화된 블록체인 기술은 여태까지의 정보들을 바꿀 수 없는 형태로 저장하여 영원히 기록되도록 한다. + +--- + +## 문제 2: 이더리움의 특징 + +이더리움이 비트코인과 다른 점은 무엇인가요? 스마트 컨트랙트의 관점에서 설명해주세요. + +**답변:** +[여기에 답변을 작성하세요] +비트코인은 값의 거래에 초점을 둔 시스템으로, 거래 기록을 관리하는 은행 장부에 가깝다. 반면 이더리움은 단순한 값의 이동을 넘어서, transaction이 smart contract 코드를 실행키며 블록체인 위에서 프로그램을 동작시킨다. 이런 점에서 이더리움은 계산과 로직이 수행되는 스프레드시트에 비유할 수 있다. +--- + +## 문제 3: Solidity 기초 + +다음 Solidity 코드에서 `public`과 `view` 키워드의 의미를 각각 설명하세요. + +```solidity +function count() public view returns (uint256) { + return _count; +} +``` + +**답변:** +- `public`: Visibility와 Access를 설정, 자동으로 getter함수를 생성한다. +- `view`:State Mutability를 설정 Read-Only 기능을 추가 - 변경은 할 수 없지만 blockchain을 조회할 수 있다 이렇게 함으로써 gas를 내지 않는다. 하지만 view를 활용하지 않는다면, 조회할 때 Gas를 지불해야 한다. + +--- + +## 문제 4: 상태 변수 + +Solidity에서 상태 변수(state variable)와 지역 변수(local variable)의 차이점을 설명하세요. + +**답변:** +[여기에 답변을 작성하세요] +| 구분 | **State Variables (상태 변수, 영구 데이터)** | **Local Variables (지역 변수, 임시 데이터)** | +|------|---------------------------------------------|---------------------------------------------| +| **저장 위치 (Storage Location)** | `storage`에 저장되며 World State의 일부 | `memory` 또는 stack에 저장되며 영구 저장소에 기록되지 않음 | +| **지속성 (Persistence)** | 트랜잭션이 끝난 후에도 블록체인에 영구적으로 저장됨 | 함수 실행 동안만 존재하고, 실행이 끝나면 삭제됨 | +| **Gas 비용 (Gas Cost)** | 값을 쓰거나 수정할 때 **매우 비쌈** (모든 노드가 상태를 갱신해야 함) | 블록체인의 영구 상태를 변경하지 않으므로 상대적으로 **저렴함** | +| **선언 위치 (Declaration)** | 함수 밖, 컨트랙트 내부에 선언 | 함수 내부에서 선언 | +--- + +## 문제 5: Gas 개념 + +이더리움에서 Gas란 무엇이며, 왜 필요한가요? + +**답변:** +[여기에 답변을 작성하세요] +Gas란 일종의 수수료이다. Transaction을 기록했을 때 성립하는지의 여부를 validator들이 판단을 해야하는데 그 validator들에게 보수로 들어가게 되는 비용이라고 볼 수 있다. ++ EVM 연산 사용량을 측정하는 단위라고 볼 수 있다. +--- + +**제출 방법:** +1. 이 파일을 복사하여 `quiz-01-solution.md`로 저장 +2. 각 문제에 대한 답변 작성 +3. Git으로 커밋 및 푸시 +4. Pull Request 생성