PDAO위키
최근 변경
최근 토론
임의 문서
도구
최근 변경
State Machine
(편집 요청)
닫기
RAW 편집
미리보기
[include(틀:Solidity 디자인 패턴)] [[분류:블록체인]] [[분류:스마트 컨트랙트]] [[분류:Solidity]] [[분류:디자인 패턴]] {{{+2 '''State Machine 패턴'''}}} [목차] == 개요 == State Machine 패턴은 컨트랙트의 생애 주기를 유한한 상태(state)들로 나누고, 각 상태에서 허용하는 행위와 전이(transition) 규칙을 명시적으로 정의하는 패턴이다.[*1 Wohrer, Maximilian; Zdun, Uwe (2018). [[https://ieeexplore.ieee.org/document/8327565|Smart Contracts: Security Patterns in the Ethereum Ecosystem and Solidity]]. ''2018 International Workshop on Blockchain Oriented Software Engineering (IWBOSE)''. IEEE. pp. 2–8.] 컴퓨터 과학의 [[유한 상태 기계]](FSM) 이론에 기반한다.[*2 Hopcroft, John E.; Ullman, Jeffrey D. (1979). ''Introduction to Automata Theory, Languages, and Computation''. Addison-Wesley. ISBN 0-201-02988-X.] 컨트랙트는 반드시 하나의 상태에만 존재하며 사전에 정의된 규칙에 따라서만 상태가 변경된다. == 동기 == 스마트 컨트랙트는 배포 후 코드 수정이 불가능하다. 그런데 경매, 에스크로, 거버넌스 등 대부분 온체인 비즈니스 로직은 '''여러 단계를 순서대로 거치는 생애주기'''를 가진다. 경매에서 정산이 끝난 뒤 입찰을 넣거나 에스크로에서 입금 전에 물건 수령을 확인하는 것은 허용되면 안 된다. 이러한 "잘못된 시점에 잘못된 함수가 호출되는" 버그는 전통 소프트웨어에서는 패치로 해결할 수 있지만 불변(immutable)한 스마트 컨트랙트에서는 자금 손실로 직결된다.[*3 Atzei, Nicola; Bartoletti, Massimo; Cimoli, Tiziana (2017). [[https://link.springer.com/chapter/10.1007/978-3-662-54455-6_8|A Survey of Attacks on Ethereum Smart Contracts (SoK)]]. ''Principles of Security and Trust''. Springer. pp. 164–186.] State Machine 패턴은 이 문제를 해결하기 위해 설계되었다. 이 패턴이 해결하는 핵심 문제는 다음과 같다. * '''단계 간 순서 강제''' - 정의되지 않은 경로로의 상태 전이를 구조적으로 차단한다. * '''함수 호출 시점 제어''' - 각 함수가 실행 가능한 상태를 컴파일 타임에 선언하여, 잘못된 시점의 호출을 런타임에서 revert한다. * '''시간 조건 자동 적용''' - 블록체인에 cron이 없는 환경에서 시간 경과에 따른 단계 전환을 트랜잭션 호출 시점에 자동 반영한다. * '''감사 용이성''' - 상태와 전이를 다이어그램으로 시각화할 수 있어 코드 리뷰와 보안 감사가 용이해진다.[*1] == 구성 요소 == === 상태 정의 - enum === {{{ enum Stage { Created, Bidding, Revealing, Settled } Stage public currentStage = Stage.Created; }}} 내부적으로 {{{uint8}}}로 저장된다. 정의되지 않은 값으로의 캐스팅은 revert된다. (Solidity 0.8+)[*4 Solidity Documentation. [[https://docs.soliditylang.org/en/latest/types.html#enums|Enums]]. docs.soliditylang.org.] === 상태 검증 - modifier === {{{ modifier atStage(Stage expected) { require(currentStage == expected, "Invalid stage"); _; } }}} 함수에 {{{atStage(Stage.Bidding)}}}을 붙이면 Bidding 상태에서만 실행된다. === 전이 함수 === '''선형 전이''' - 순서대로만 진행: {{{ function nextStage() internal { currentStage = Stage(uint(currentStage) + 1); } }}} '''명시적 전이''' - 허용된 경로만: {{{ function transitionTo(Stage next) internal { require(allowedTransitions[currentStage][next]); currentStage = next; } }}} === 시간 기반 전이 - timedTransition === 블록체인에는 cron이 없으므로 다음 트랜잭션 호출 시 시간 경과를 검사한다.[*5 Solidity Documentation. [[https://docs.soliditylang.org/en/latest/common-patterns.html#state-machine|Common Patterns: State Machine]]. docs.soliditylang.org.] {{{ modifier timedTransition() { if (currentStage == Stage.Bidding && block.timestamp >= createdAt + BIDDING_DURATION) currentStage = Stage.Revealing; if (currentStage == Stage.Revealing && block.timestamp >= createdAt + BIDDING_DURATION + REVEAL_DURATION) currentStage = Stage.Settled; _; } }}} {{{#!wiki style="border-left: 3px solid #edab00; background-color: #fef6e7; padding: 10px 15px; margin: 10px 0;" '''주의''': {{{if}}}를 연속 사용한다({{{else if}}} 아님). 오랜 기간 트랜잭션이 없었을 때 한 번의 호출로 연쇄 전이가 가능해야 하기 때문이다. }}} === 이벤트 === {{{ event StageChanged(Stage indexed from, Stage indexed to, address indexed triggeredBy); }}} 모든 상태 전이에서 이벤트를 발행하여 오프체인 추적을 가능하게 한다. == 구현 패턴 == === 선형 전이 === 상태가 순차적으로만 진행되는 경우. {{{nextStage()}}}로 {{{uint(currentStage) + 1}}} 계산. 경매, ICO, 투표, 타임락에 적합하다. === 비선형 전이 - 전이 테이블 === 분기·복귀가 있는 워크플로우에서 사용한다. 허용된 전이를 mapping으로 정의한다.[*1] {{{ mapping(Stage => mapping(Stage => bool)) private allowedTransitions; constructor() { allowedTransitions[Stage.AwaitingPayment][Stage.Funded] = true; allowedTransitions[Stage.Funded][Stage.Delivered] = true; allowedTransitions[Stage.Funded][Stage.Disputed] = true; allowedTransitions[Stage.Disputed][Stage.Delivered] = true; // resolve allowedTransitions[Stage.Disputed][Stage.Cancelled] = true; // cancel } }}} === 비트마스크 최적화 === 이중 mapping은 검증당 SLOAD 2회가 필요하다. 비트마스크를 사용하면 SLOAD 1회 + 비트 연산으로 줄일 수 있다.[*6 Ethereum Yellow Paper. [[https://ethereum.github.io/yellowpaper/paper.pdf|Ethereum: A Secure Decentralised Generalised Transaction Ledger]]. Appendix G. Fee Schedule.] {{{ mapping(Stage => uint8) private transitionMap; // AwaitingPayment -> Funded(bit 1) 허용 transitionMap[Stage.AwaitingPayment] = 1 << uint8(Stage.Funded); function transitionTo(Stage next) internal { require(transitionMap[currentStage] & (1 << uint8(next)) != 0); currentStage = next; } }}} ||<tablealign=center><tablewidth=80%><tablebordercolor=#a2a9b1><tablebgcolor=#f8f9fa> '''방식''' || '''가스 비용 (검증 1회)''' || '''적합한 경우''' || || 이중 mapping || ~4,200 gas (SLOAD × 2) || 가독성 우선 || || 비트마스크 || ~2,100 gas (SLOAD × 1) || 호출 빈번, 가스 최적화 필요 || || 선형 nextStage() || ~200 gas (연산만) || 순차 전이만 있을 때 || == 보안 고려사항 == === Reentrancy (재진입 공격) === 상태 전이 함수가 외부 컨트랙트에 ETH를 송금하거나 외부 함수를 호출할 때, 호출받은 컨트랙트가 원래 함수를 다시 호출(재진입)할 수 있다. 이때 상태가 아직 변경되지 않았다면, {{{atStage}}} modifier를 통과하여 동일한 함수가 반복 실행된다.[*3] 대응은 Checks-Effects-Interactions(CEI) 패턴이다. 상태 변경(Effect)을 외부 호출(Interaction)보다 먼저 수행하면, 재진입 시 이미 변경된 상태에 의해 {{{atStage}}}에서 revert된다.[*7 ConsenSys Diligence. [[https://consensysdiligence.github.io/smart-contract-best-practices/attacks/reentrancy/|Reentrancy]]. ''Smart Contract Best Practices''.] === block.timestamp 조작 === 시간 기반 전이({{{timedTransition}}})는 {{{block.timestamp}}}에 의존한다. 블록 제안자는 이 값을 실제 시간 대비 약 15초 범위 내에서 조작할 수 있다.[*8 ConsenSys Diligence. [[https://consensysdiligence.github.io/smart-contract-best-practices/development-recommendations/solidity-specific/timestamp-dependence/|Timestamp Dependence]]. ''Smart Contract Best Practices''.] 따라서 각 단계의 최소 길이를 수 분 이상으로 설정해야 한다. === Front-running === 상태 전이를 발생시키는 트랜잭션이 mempool에 공개되면, 공격자가 이를 관찰하고 더 높은 가스비로 자신의 트랜잭션을 먼저 포함시킬 수 있다.[*9 Daian, Philip et al. (2020). [[https://www.computer.org/csdl/proceedings-article/sp/2020/349700b910/1j2LgsZYg2A|Flash Boys 2.0]]. ''2020 IEEE Symposium on Security and Privacy''. pp. 910–927.] 대응으로는 commit-reveal 스킴이나 전이 전 최소 지연 시간을 두는 방법이 있다. == 활용 사례 == ||<tablealign=center><tablewidth=80%><tablebordercolor=#a2a9b1><tablebgcolor=#f8f9fa> '''분야''' || '''예시''' || '''상태 흐름''' || || DeFi 거버넌스 || Aave, Compound Governor || Pending → Active → Succeeded/Defeated → Queued → Executed || || NFT 민팅 || ERC-721 드롭 || Inactive → Whitelist → Public → SoldOut → Revealed || || 에스크로 || 결제 보호 || AwaitingPayment → Funded → Delivered → Complete || || 공급망 || 제품 추적 || Manufactured → Shipped → InTransit → Delivered → Verified || || 스테이킹 || PoS 프로토콜 || Unlocked → Staked → Cooldown → Withdrawable || == 관련 패턴 == * [[Guard Check 패턴]] - {{{require}}}/{{{modifier}}}를 사용한 사전 조건 검증. State Machine의 {{{atStage}}}가 이에 해당한다. * [[Access Restriction 패턴]] - 역할 기반 접근 제어. State Machine과 결합하여 "누가 + 언제" 조건을 완성한다. * [[Checks-Effects-Interactions 패턴]] - 상태 변경 후 외부 호출. State Machine의 전이 보안에 필수적이다. * [[Pull over Push 패턴]] - {{{withdraw()}}} 패턴. 상태 전이와 자금 인출을 분리하여 안전성을 높인다. == 관련 문서 == * [[블록체인]] * [[스마트 컨트랙트]] * [[Solidity]] * [[이더리움]] * [[유한 상태 기계]] * [[디자인 패턴]]
요약
문서 편집을
저장
하면 당신은 기여한 내용을
CC BY-SA 4.0
으로 배포하고 기여한 문서에 대한 하이퍼링크나 URL을 이용하여 저작자 표시를 하는 것으로 충분하다는 데 동의하는 것입니다. 이
동의는 철회할 수 없습니다.
비로그인 상태로 편집합니다. 로그인하지 않은 상태로 문서 편집을 저장하면, 편집 역사에 본인이 사용하는 IP(216.73.217.42) 주소 전체가 영구히 기록됩니다.
저장
닫기
Liberty
|
the tree