개요

Ethereum에서 트랜잭션 순서를 보장하는 핵심 메커니즘은 nonce임. 네트워크에서 자주 겪는 “nonce 꼬임"은 대개 트랜잭션 상태와 nonce 소비 규칙을 혼동해서 발생함. 아래는 EOA와 트랜잭션 관계, nonce 정의, 트랜잭션 상태별 nonce 변화 정리

EOA와 트랜잭션

Ethereum 계정은 두 종류로 구분

  • EOA, 개인 키로 서명해 트랜잭션 전송 가능
  • Contract Account, 스스로 트랜잭션 전송 불가, 외부에서 온 트랜잭션의 실행 흐름 내에서만 호출됨

트랜잭션을 보낸다는 것은 EOA가 서명 후 네트워크에 브로드캐스트한다는 의미. 컨트랙트는 내부 호출과 생성 같은 메시지 호출을 발생시킬 수 있지만 이는 트랜잭션이 아님. 최상위 트랜잭션의 발신자는 항상 EOA

Nonce 정의와 규칙

Nonce는 특정 EOA가 보낸 트랜잭션의 순서를 보장하기 위한 계수. 해당 EOA가 블록에 포함시킨 트랜잭션 개수와 동일하게 증가

  • 새 주소의 첫 트랜잭션 nonce는 0
  • 이후 블록에 포함될 때마다 1씩 증가

핵심은 성공 여부가 아니라 실행 여부. 블록에 포함되어 EVM이 실행한 순간 nonce는 소비됨. 성공이든 revert든 동일하게 소비됨

실행 조건은 단순함

  • tx.nonce == account.nonce 일 때만 해당 트랜잭션 실행 가능
  • 앞 nonce가 소비되지 않으면 뒤 nonce는 대기 상태. 연속 nonce 충족 필요

예시 표현

  • 트랜잭션이 블록에 포함되면 nonce 1 증가

트랜잭션의 4가지 상태

트랜잭션은 다음 네 가지 상태 중 하나로 귀결

  1. Pending
  • 노드가 수신 후 mempool에 보관 중
  • 블록에 포함되지 않음, 실행 기록 없음
  1. Mined + Success
  • 블록에 포함되어 정상 실행
  • 상태 변경 반영, nonce 소비
  1. Mined + Revert
  • 블록에 포함되어 실행되었으나 조건 불충족 또는 out-of-gas 등으로 revert
  • 상태 변경은 롤백되나 실행 사실은 유효. nonce 소비
  1. Dropped
  • 블록에 포함되지 않음, mempool에서 제거됨
  • 실행되지 않았으므로 nonce 미소비

상태별 nonce 소비 정리

기준은 블록 포함 후 실행 여부 한 가지

  • Pending, Dropped는 블록 미포함 → 실행 아님 → nonce 미소비 → 다음 nonce 사용 불가
  • Mined + Success, Mined + Revert는 블록 포함 및 실행 → nonce 소비 → 다음 nonce 사용 가능

연속 nonce 규칙 때문에 앞선 nonce가 처리되지 않으면 동일 계정의 이후 트랜잭션은 실행 불가. 뒤 nonce가 mempool에 대기할 수는 있지만 블록에서 실행되려면 앞 nonce가 먼저 소비되어야 함

정리와 시사점

  • 트랜잭션은 EOA만 전송. 컨트랙트는 외부 트랜잭션에 의해 호출되는 대상
  • Nonce는 EOA 단위 트랜잭션 순서를 나타내는 카운터. 0에서 시작해 블록 포함 시 1씩 증가
  • 성공과 revert를 구분하지 않음. 블록에 포함되어 실행되면 nonce 소비
  • Pending이나 Dropped는 실행 사실이 없으므로 nonce 변화 없음. 앞 nonce 미소비 시 뒤 nonce 트랜잭션은 절대 실행 불가
  • Nonce는 트랜잭션 순서를 강제하고 동일 계정 내 재전송 재생 공격을 억제하는 역할 수행

원하면 본 구조 그대로 도식, RPC 호출 예시, viem 또는 ethers를 이용한 상태 판별 코드 스니펫까지 확장 가능

참고자료