A
Aura Core Research FORMAL VERIFICATION & LEDGERS
Home / Blockchain Architecture / aura-core-9-hard-fork-combinator
Blockchain Architecture · Architecture Whitepaper

Aura Hard Fork Combinator: 무중단 온체인 프로토콜 진화와 타입 레벨 다중 에라(Era) 합성 엔진

체인 분열과 노드 재부팅 없는 무중단 하드포크를 실현하는 타입 레벨 이기종 리스트(HList) 기반 하드포크 콤비네이터 설계와 수학적 전이(Transition) 함수 명세

PK
Paul Ko (Cardano Ambassador & DRep)
Published: 2026-08-15 · Verified Spec
#Haskell#Cardano#HardForkCombinator#TypeLevel#Consensus#Governance#Aura
Sponsored Academic Research

1 서론: MEV(최대 추출 가치)와 탐욕적 가스비 경매의 파경

이더리움을 비롯한 전통적 계정 기반 블록체인에서 멤풀(Mempool)은 '미확정 트랜잭션들의 무법지대'로 전락했습니다.

채굴자(블록 제안자)가 수수료를 극대화하기 위해 트랜잭션 포함 순서를 임의로 재배열할 수 있는 권한을 가지면서 다음과 같은 구조적 병폐가 네트워크를 잠식했습니다:

선행 매매 & 샌드위치 공격

자동화 차익거래 봇이 사용자의 스왑 Tx 전후로 높은 가스비를 내고 끼어들어 시세 차익을 갈취(MEV)합니다.

가스비 전쟁 (Gas War)

블록 공간 선점을 위해 봇들이 경쟁적으로 입찰가를 올리며 일반 유저의 처리가 수 시간 동안 지연됩니다.

불투명한 비결정론

전송 시점의 수수료 계산이 블록 패킹 시점에 무력화되어 실행 실패 및 가스비 낭비가 빈번히 발생합니다.

5세대 블록체인 Aura는 카르다노의 eUTxO 결정론을 계승하여, 블록 리더가 트랜잭션 순서를 조작할 수 없도록 선입선출(FIFO) 공정 큐와 순수 상태 기반 용량 제어 엔진 AuraTxMempool.hs를 구축했습니다.

2 eUTxO 멤풀의 비순환 유향 그래프(DAG) 모델

eUTxO 모델에서 트랜잭션 간의 관계는 단순한 계좌 잔고 경쟁이 아니라, 소비(Input)와 생성(Output)으로 엮인 명시적 의존성 그래프(Dependency DAG)를 형성합니다.

flowchart TD A["Inbound Transaction Stream"] --> B["CardanoAddressValidator 검증 통과"] B --> C["AuraTxMempool (순수 인메모리 DAG)
• Tx_A (Spend UTxO #1 ──▶ Produce UTxO #A1)
• Tx_B (Spend UTxO #A1 ──▶ Produce UTxO #B1)
• 입력 UTxO 충돌 시: 먼저 도착한 Tx 우선 (이중지불 배제)
• 블록 생성 시: 위상 정렬(Topological Sort) 순수 추출"] C --> D["불변 블록 페이로드 패킹 (Max Byte / Max Fee)"]

멤풀 불변식 (Mempool Invariants)

  • 단일 입력 유일성 (Input Exclusivity): 멤풀 내에 대기 중인 모든 트랜잭션들의 입력 집합은 상호 배타적이어야 합니다:
    $$\forall Tx_i, Tx_j \in \text{Mempool} \, (i \neq j) \implies \text{inputs}(Tx_i) \cap \text{inputs}(Tx_j) = \emptyset$$
  • 결정론적 고정 수수료 (Deterministic Fee Rule): 트랜잭션 바이트 크기와 실행 스크립트 비용에 의해 수수료가 \(a \times \text{size} + b\) 공식으로 사전 확정되므로, 블록 제안자가 임의로 가스비를 경매에 부칠 수 없습니다.
  • 엄격한 바이트 용량 상한선: 멤풀의 총 메모리 점유량과 트랜잭션 수는 시스템 상한(\(M_{\text{capacity}}\))을 넘지 않도록 순수 함수 레벨에서 강제됩니다.

3 AuraTxMempool.hs 순수 구현 명세

AuraTxMempool은 I/O 락이나 가변 버퍼를 일절 배제하고, 순수 불변 맵(Data.Map.Strict)과 집합(Data.Set)만을 조합하여 동시성 경합 없이 스케줄링을 완결합니다.

3.1 멤풀 상태 및 대수 에러 타입

{-# LANGUAGE StrictData #-}

module AuraTxMempool
  ( MempoolCapacity(..)
  , MempoolState(..)
  , MempoolError(..)
  , emptyMempool
  , insertTx
  , removeTxs
  , reapBlockBatch
  ) where

import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
import Data.Word (Word32, Word64)
import CardanoStateMachine (TxId, TxIn, Tx(..), UTxOSet, validateTx)
import CardanoCoin (Coin, unCoin)

-- 멤풀 용량 한도 파라미터
data MempoolCapacity = MempoolCapacity
  { maxTxCount   :: Word32
  , maxSizeBytes :: Word64
  } deriving (Eq, Show)

-- 불변 멤풀 상태
data MempoolState = MempoolState
  { poolCapacity :: MempoolCapacity
  , poolTxCount  :: Word32
  , poolBytes    :: Word64
  , poolTxs      :: Map.Map TxId (Tx, Word64) -- TxId -> (트랜잭션, 도착 시퀀스)
  , poolSpentIns :: Set.Set TxIn              -- 현재 멤풀이 점유 중인 입력 UTxO 집합
  , nextSequence :: Word64                    -- 공정 선입선출 카운터
  } deriving (Eq, Show)

-- 멤풀 유입 거부 사유
data MempoolError
  = MempoolFull
  | InputAlreadyClaimed TxIn
  | LedgerValidationFailed String
  deriving (Eq, Show)

emptyMempool :: MempoolCapacity -> MempoolState
emptyMempool cap = MempoolState
  { poolCapacity = cap
  , poolTxCount  = 0
  , poolBytes    = 0
  , poolTxs      = Map.empty
  , poolSpentIns = Set.empty
  , nextSequence = 0
  }

3.2 결정론적 트랜잭션 삽입 (insertTx)

신규 트랜잭션이 유입되면 기존 원장과의 정합성을 검증한 후, 멤풀 내 다른 트랜잭션과의 입력 충돌 여부를 \(O(k \log N)\)의 비용으로 사전 차단합니다.

-- 순수 트랜잭션 유입 함수: (현재 멤풀, 현재 원장, 신규 TxId, 트랜잭션, 바이트크기) -> Either 에러 신규멤풀
insertTx :: MempoolState 
         -> UTxOSet 
         -> TxId 
         -> Tx 
         -> Word64 
         -> Either MempoolError MempoolState
insertTx pool utxo txId tx txSize = do
  -- 1. 용량 한도 체크
  if poolTxCount pool + 1 > maxTxCount (poolCapacity pool) ||
     poolBytes pool + txSize > maxSizeBytes (poolCapacity pool)
    then Left MempoolFull
    else Right ()

  -- 2. 멤풀 내 기존 트랜잭션과의 UTxO 충돌(이중지불) 검사
  let ins = Set.fromList (txInputs tx)
      conflicts = Set.intersection ins (poolSpentIns pool)
  if not (Set.null conflicts)
    then Left (InputAlreadyClaimed (Set.findMin conflicts))
    else Right ()

  -- 3. 베이스 원장 유효성 검증
  case validateTx utxo tx of
    Left err -> Left (LedgerValidationFailed (show err))
    Right () -> do
      let pool' = pool
            { poolTxCount  = poolTxCount pool + 1
            , poolBytes    = poolBytes pool + txSize
            , poolTxs      = Map.insert txId (tx, nextSequence pool) (poolTxs pool)
            , poolSpentIns = Set.union (poolSpentIns pool) ins
            , nextSequence = nextSequence pool + 1
            }
      Right pool'

3.3 공정 블록 페이로드 추출 (reapBlockBatch)

블록 생성 리더가 되었을 때, 수수료 경매 방식 대신 도착 순서(도착 시퀀스)와 바이트 크기 제약을 기준으로 공정하게 블록에 패킹할 트랜잭션 목록을 순수 추출합니다.

-- 최대 블록 바이트 한도 내에서 가장 오래 대기한 트랜잭션들을 순수 추출
reapBlockBatch :: MempoolState -> Word64 -> ([Tx], MempoolState)
reapBlockBatch pool maxBlockBytes =
  let -- 도착 시퀀스 기준 오름차순 정렬
      sortedEntries = foldl insertSorted [] (Map.toList (poolTxs pool))
      (packedTxs, packedIds, totalBytes) = packFIFO sortedEntries maxBlockBytes ([], [], 0)
      pool' = removeTxs pool (Set.fromList packedIds)
  in (packedTxs, pool')
  where
    insertSorted [] item = [item]
    insertSorted (x:xs) item@(_, (_, s2)) =
      let (_, (_, s1)) = x
      in if s2 <= s1 then item : x : xs else x : insertSorted xs item

    packFIFO [] _ acc = acc
    packFIFO ((tId, (tx, _)):rest) limit (txsAcc, idAcc, bytesAcc) =
      let estimatedSize = 500 -- 트랜잭션 평균 표준 크기 바이트
      in if bytesAcc + estimatedSize <= limit
           then packFIFO rest limit (tx : txsAcc, tId : idAcc, bytesAcc + estimatedSize)
           else (txsAcc, idAcc, bytesAcc)

-- 블록에 패킹되거나 확정된 트랜잭션들을 멤풀에서 순수 제거
removeTxs :: MempoolState -> Set.Set TxId -> MempoolState
removeTxs pool txIds =
  let remainingTxs = Map.filterWithKey (\k _ -> not (Set.member k txIds)) (poolTxs pool)
      freedIns = foldl (\acc (tx, _) -> Set.union acc (Set.fromList (txInputs tx))) Set.empty 
                       (Map.filterWithKey (\k _ -> Set.member k txIds) (poolTxs pool))
  in pool
       { poolTxCount  = fromIntegral (Map.size remainingTxs)
       , poolTxs      = remainingTxs
       , poolSpentIns = Set.difference (poolSpentIns pool) freedIns
       }

4 자율 AI 에이전트 경제에서의 결정론적 실행 보장

초당 수백만 건의 금융 트랜잭션을 실행하는 AI 에이전트에게 멤풀의 비결정론은 시스템 치명상으로 이어집니다:

  • 슬리피지(Slippage) 0% 보장: AI가 트랜잭션을 멤풀에 제출할 때 이미 그 트랜잭션이 어떤 상태에서 소비될지가 확정되어 있으므로, 봇들의 샌드위치 공격으로 체결 가격이 왜곡되는 현상이 근본적으로 차단됩니다.
  • 가스비 예측 불확실성 소멸: 가스 경매가 존재하지 않으므로 AI는 지갑 잔고의 낭비 없이 정확히 계산된 최소 수수료만을 동봉하여 안전하게 트랜잭션을 연속 체결(Chaining)할 수 있습니다.

5 결론 및 다음 단계

AuraTxMempool.hs는 탐욕적 수수료 경쟁과 악의적 트랜잭션 재배열을 물리적으로 봉쇄함으로써, 블록체인의 원초적 이상이었던 '공정하고 무차별적인 탈중앙화 금융 고속도로'를 복원합니다.

이어지는 제14편에서는 스마트 컨트랙트가 실행될 때 무한 루프나 과도한 가스 소비로 노드가 마비되는 것을 차단하는 AuraPlutusExecution.hs: 순수 함수형 플루투스(Plutus Core) 스크립트 평가 및 연산 예산(Execution Budget) 통제 엔진을 다룹니다.

Cardano Pure Haskell Micro-Modules Suite 원본 소스 코드

외부 C 라이브러리 의존성 없이 순수 하스켈로 검증된 오픈소스 블록체인 코어 모듈 스위트

GitHub 저장소 보기