메인 콘텐츠로 건너뛰기

컨트랙트 런타임

Savitri 컨트랙트 런타임은 가스 계량, 스토리지 격리, 이벤트 방출을 갖춘 격리된 환경(샌드박스)에서 스마트 컨트랙트를 실행합니다.

실행 흐름

컨트랙트 호출 TX 도착


BaseContract 검증
│ paused, owner, 예약된 슬롯 확인

Gas Meter 초기화
│ TX 수수료에서 gas_limit 결정

ContractStorage 설정
│ 컨트랙트 상태 로드, 캐시 초기화

함수 디스패치
│ function_selector → 핸들러 매핑

실행 (가스 추적 포함)
│ SLOAD, SSTORE, 이벤트, 호출

결과
├── 성공 → 스토리지 커밋, 이벤트 방출
└── 실패 → 모든 변경 사항 롤백

BaseContract

모든 컨트랙트는 BaseContract를 확장합니다. 슬롯 0-99는 예약되어 있습니다:

슬롯필드크기설명
0owner32바이트컨트랙트 소유자 주소
1versionu64컨트랙트 버전
2governance_hookbool거버넌스 통합 활성화
3fee_hookbool사용자 정의 수수료 로직 활성화
4pausedbool컨트랙트 일시 정지 상태
5-99예약됨-향후 사용

BaseContract 함수

함수접근설명
owner()공개컨트랙트 소유자 조회
transfer_ownership(new_owner)소유자 전용소유권 이전
version()공개컨트랙트 버전 조회
upgrade(new_version)소유자 전용컨트랙트 업그레이드
pause()소유자 전용모든 작업 일시 정지
unpause()소유자 전용작업 재개

가스 계량

가스 비용

연산가스설명
SLOAD100스토리지 읽기
SSTORE (새로운)20,000스토리지 쓰기 (빈 슬롯)
SSTORE (업데이트)5,000스토리지 쓰기 (기존 슬롯)
CALL2,300크로스 컨트랙트 호출
CREATE32,000컨트랙트 배포
TRANSFER300토큰 전송
LOG375이벤트 방출 (기본)
LOG_TOPIC375추가 토픽당
LOG_DATA8이벤트 데이터 바이트당
CALLDATA16호출 데이터 바이트당

블록 단위 계산

가스 계량기는 오버헤드를 줄이기 위해 블록 단위 계산 (블록당 100 연산)을 사용합니다. 가스는 개별 연산 단위가 아닌 블록 단위로 계산됩니다.

가스 한도

가스 한도는 트랜잭션 수수료에서 파생됩니다:

gas_limit = tx.fee / gas_price

실행 중 가스가 소진되면 전체 트랜잭션이 롤백됩니다.

컨트랙트 스토리지

슬롯 기반 모델

각 컨트랙트는 키가 u64 슬롯 번호이고 값이 32바이트 배열인 독립적인 키-값 저장소를 가집니다.

슬롯 파생

매핑의 경우, 충돌을 방지하기 위해 Keccak256을 통해 슬롯이 파생됩니다:

// 단순 값
slot = FIXED_SLOT_NUMBER

// 매핑 (address → value)
slot = keccak256(address || base_slot)[0..8] as u64

// 중첩 매핑 (address → address → value)
inner = keccak256(outer_key || base_slot)
slot = keccak256(inner_key || inner)[0..8] as u64

머클 트리

컨트랙트 스토리지는 상태 증명 생성을 위한 머클 트리를 유지합니다.

이벤트 시스템

컨트랙트는 외부 소비자를 위한 이벤트를 방출합니다:

pub struct CustomEvent {
pub event_type: String, // 예: "Transfer", "Approval"
pub topics: Vec<Vec<u8>>, // 인덱싱된 필드
pub data: Vec<u8>, // 인덱싱되지 않은 데이터
}

표준 이벤트 (BaseContract에서):

  • OwnershipTransferred(old_owner, new_owner)
  • ContractUpgraded(old_version, new_version)
  • Paused(by)
  • Unpaused(by)

컨트랙트 배포

1. DeployTransaction 생성 (to = None, data = 바이트코드)
2. 컨트랙트 주소 할당 (배포자 + 논스에서 파생)
3. BaseContract 초기화 (owner, version 설정)
4. 컨트랙트 생성자 실행 (initialize 함수)
5. CF_CONTRACTS에 ContractInfo 저장
6. ContractDeployed 이벤트 방출

컨트랙트 호출

1. TX 데이터에서 function_selector 파싱
2. CF_CONTRACTS에서 컨트랙트 로드
3. BaseContract 상태 확인 (일시 정지 안됨 등)
4. gas_limit으로 GasMeter 초기화
5. ContractStorage로 함수 실행
6. 성공 시: 스토리지 변경 사항 커밋, 가스 공제
7. 실패 시: 모든 변경 사항 롤백

EVM 인터프리터

evm_interpreter 모듈은 Ethereum 컨트랙트 호환성을 위한 기본 EVM 바이트코드 실행을 제공합니다. 이를 통해 Solidity로 컴파일된 컨트랙트를 Savitri에 배포할 수 있습니다.

메모리 모니터링

memory_monitor는 DoS 공격 방지를 위해 런타임 메모리 사용량을 추적합니다:

  • 컨트랙트당 메모리 제한
  • 할당 추적
  • 한도 초과 시 자동 종료

병렬 실행

parallel 모듈은 독립적인 트랜잭션에 대한 컨트랙트 병렬 실행을 가능하게 합니다:

  • 트랜잭션 간 의존성 분석
  • 스토리지 슬롯 충돌 감지
  • 비충돌 TX의 병렬 실행
  • 충돌 TX에 대한 직렬 폴백

컨트랙트 추적

tracing 모듈은 디버깅을 위한 실행 추적을 제공합니다:

  • 단계별 실행 로그
  • 스토리지 읽기/쓰기 추적
  • 연산별 가스 소비
  • 이벤트 방출 로그