메인 콘텐츠로 건너뛰기

목적 및 범위

이 문서는 StableNet에서 사용되는 트랜잭션의 데이터 구조, 타입 계층, 인코딩 방식을 설명합니다.
지원되는 6가지 트랜잭션 타입과 각 타입의 역할, 인코딩 형식(RLP, 바이너리, JSON), 서명 및 해시 계산 방식을 다룹니다.
관련 문서:

트랜잭션 타입 계층 구조

StableNet은 다형성 기반 타입 계층 구조를 통해 6가지 트랜잭션 타입을 지원합니다.
모든 트랜잭션 타입은 TxData 인터페이스를 구현하며, 상위 Transaction 구조체로 래핑됩니다.

타입 목록

타입상수설명
LegacyLegacyTxType0x00EIP-2718 이전 형식
Access ListAccessListTxType0x01EIP-2930 접근 목록 트랜잭션
Dynamic FeeDynamicFeeTxType0x02EIP-1559 동적 수수료
BlobBlobTxType0x03EIP-4844 데이터 가용성 트랜잭션
Set CodeSetCodeTxType0x04EIP-7702 코드 위임
Fee DelegatedFeeDelegateDynamicFeeTxType0x16 (22)StableNet 수수료 위임 트랜잭션

TxData 인터페이스

모든 트랜잭션 타입이 구현하는 TxData 인터페이스(core/types/transaction.go)는 다음 책임을 가집니다.
  • 타입 식별: txType() byte
  • 필드 접근자: chainID(), nonce(), gas(), gasPrice(), gasTipCap(), gasFeeCap(), value(), to(), data(), accessList()
  • 서명 처리: rawSignatureValues(), setSignatureValues()
  • 수수료 위임 확장: feePayer(), rawFeePayerSignatureValues()
  • 가스 계산: effectiveGasPrice(baseFee, gasTip)
  • 인코딩/디코딩: encode(*bytes.Buffer), decode([]byte)

Transaction 래퍼

Transaction 구조체는 모든 트랜잭션 타입을 감싸는 공통 래퍼로 다음 역할을 수행합니다.
  • 타입 무관 인터페이스 제공: 내부에 TxData 구현 보유
  • 최초 관측 시점 기록: 로컬 노드에서 처음 인지한 시각 저장
  • 계산 결과 캐시: 해시, 크기, 발신자/수수료 지불자 주소를 atomic.Value로 캐시
  • 불변성 보장: 생성 이후 수정 불가, 변경 시 새 트랜잭션 인스턴스 필요

개별 트랜잭션 타입

레거시 트랜잭션 (타입 0x00)

EIP-2718 이전의 기본 트랜잭션 형식으로, 가장 단순한 구조를 가집니다.
필드타입설명
Nonceuint64계정 nonce
GasPrice*big.IntWei 단위 가스 가격
Gasuint64가스 한도
To*common.Address수신 주소 (nil이면 컨트랙트 생성)
Value*big.Int전송 값
Data[]byte호출 데이터
V, R, S*big.IntECDSA 서명 값

액세스 리스트 트랜잭션 (타입 0x01)

EIP-2930으로 도입되었으며, 트랜잭션 실행 시 접근할 주소와 저장소 키를 사전 선언합니다. 추가 특징:
  • ChainID 포함: 리플레이 공격 방지
  • AccessList 포함: 주소 및 슬롯 목록
  • 서명 포맷 단순화: yParity(0 또는 1) 사용

동적 수수료 트랜잭션 (타입 0x02)

EIP-1559 기반 트랜잭션으로, 기본 수수료와 우선순위 수수료를 분리합니다.
필드설명
GasTipCap최대 우선순위 수수료
GasFeeCap최대 총 가스 비용
유효 가스 가격은 다음과 같이 계산됩니다. min(GasTipCap + BaseFee, GasFeeCap) StableNet에서는 거버넌스 정책에 따라 GasTipCap이 블록 헤더 기준 값으로 제한될 수 있습니다.

Blob 트랜잭션 (타입 0x03)

EIP-4844로 도입된 데이터 가용성 트랜잭션입니다. 추가 필드:
  • BlobFeeCap: blob 가스당 최대 수수료
  • BlobHashes: 버전 포함 blob 커밋 해시
  • Sidecar: 실제 blob 데이터
제약사항:
  • 컨트랙트 생성 불가
  • 사이드카 데이터는 해시 계산에서 제외됨
  • blob 관련 수치는 uint256.Int 사용

Set Code 트랜잭션 (타입 0x04)

EIP-7702 기반 트랜잭션으로, EOA가 실행 코드를 위임할 수 있습니다. 추가 필드:
  • AuthList: 코드 위임 인가 목록
제약사항:
  • 컨트랙트 생성 불가
  • AuthList는 비어 있을 수 없음
  • 실행 실패 시에도 이미 처리된 위임은 롤백되지 않음

수수료 위임 트랜잭션 (타입 0x16)

StableNet 고유 확장 트랜잭션으로, 제3자가 가스 수수료를 부담합니다. 추가 필드:
  • FeePayer: 수수료 지불자 주소
  • FV, FR, FS: 수수료 지불자 서명
두 개의 서명이 필요합니다.
  1. 발신자 서명: 트랜잭션 내용 승인
  2. 수수료 지불자 서명: 가스 비용 지불 승인
상태 전이 시 수수료 지불자의 잔액만 가스 비용에 사용됩니다.

인코딩 형식

RLP 인코딩

RLP는 네트워크 전송과 저장을 위한 기본 형식입니다. 레거시 트랜잭션:
RLP([Nonce, GasPrice, Gas, To, Value, Data, V, R, S])
타입 트랜잭션:
type_byte || RLP([field1, field2, ...])
첫 바이트로 레거시/타입 트랜잭션을 구분합니다.

바이너리 인코딩

MarshalBinary() / UnmarshalBinary()는 EIP-2718 정식 바이너리 인코딩을 제공합니다.
해시 및 서명 계산에 사용됩니다.

JSON 인코딩

RPC 인터페이스에서 사용되는 사람이 읽기 쉬운 형식입니다.
  • 숫자 값은 모두 16진수 문자열
  • 타입 트랜잭션은 yParity 포함
  • vyParity는 반드시 일치해야 함

트랜잭션 서명

서명자 선택 로직

체인 설정과 블록 상태에 따라 서명자가 선택됩니다.
if IsCancun(blockNumber, blockTime):    → CancunSigner
else if AnzeonEnabled():                → AnzeonSigner
else if IsLondon(blockNumber):          → LondonSigner
else if IsBerlin(blockNumber):          → EIP2930Signer
else if IsEIP155(blockNumber):          → EIP155Signer
else if IsHomestead(blockNumber):       → HomesteadSigner
else:                                   → FrontierSigner
AnzeonSigner는 Set Code 트랜잭션을 포함한 StableNet 확장을 처리합니다.

서명 및 복구

  • 서명은 서명 필드를 제외한 해시에 대해 수행
  • Sender()를 통해 발신자 주소를 복구
  • 수수료 위임 트랜잭션은 FeePayer()로 별도 주소 복구

트랜잭션 해싱

해시 계산 규칙

레거시 트랜잭션:
keccak256(rlp([...]))
타입 트랜잭션:
keccak256(type_byte || rlp([...]))
해시는 최초 계산 후 캐시됩니다.

Blob 트랜잭션 특이사항

Blob 트랜잭션 해시는 사이드카 데이터를 포함하지 않습니다.
사이드카 유무와 무관하게 동일한 해시를 유지합니다.

상태 전이와의 통합

실행 전 트랜잭션은 TransactionToMessage()를 통해 Message 구조체로 변환됩니다.
  • 발신자 및 수수료 지불자 주소 추출
  • 가스 가격 계산
  • 수수료 위임 정보 반영
  • RPC 호출 시 서명 검증을 건너뛸 수 있는 플래그 포함
이를 통해 트랜잭션은 블록 실행과 RPC 시뮬레이션 양쪽에서 동일한 경로로 처리됩니다.