
비동기 프로그램 실행: 새로운 Solana APE 시대의 서막
소개
올해 성황리에 열린 Breakpoint 컨퍼런스는 열띤 토론과 발표로 가득했습니다. 그 와중에도 Solana 공동 창립자 Anatoly Yakovenko는 즉석에서 진행된 단 한 번의 미녹화 기술 워크숍을 위한 시간을 마련했습니다. 그는 플립 차트와 마커를 들고 자신이 Solana의 다음 진화 단계로 열정적으로 지지하는 주제이자 이 글의 핵심인 비동기 실행의 복잡한 원리를 설명했습니다.
비동기 실행(이하 AE)의 원대한 비전을 살펴보기에 앞서 AE의 핵심 개념과 목표를 개괄적으로 설명하겠습니다. 그런 다음 향후 제안된 아키텍처를 비교 분석할 기반을 마련하기 위해 Solana의 기존 실행 및 합의 메커니즘을 살펴봅니다. 이어서 이러한 전환에 필수적인 디딤돌인 Bankless Leaders부터 AE를 둘러싼 여러 제안을 다룹니다. 2022년의 초기 설계 제안부터 시작해 여러 블록 생산자가 동시에 작동하는 설계 구현까지 포괄하는 최신 "Endgame Architecture" 제안을 시간순으로 살펴보겠습니다.
AE 개요
생각해 보면 조금 이상합니다. 검증인이 하는 일은 블록을 만들고 투표하는 것뿐입니다. 그 어떤 블록도 실행할 필요가 없습니다. 블록을 만들면서도 어떤 종류의 블록을 만드는지 알지 못합니다… 중요한 것은 순서에 합의하는 것뿐입니다. 값이 무엇인지는 중요하지 않습니다.

Solana 핵심 프로토콜에서 AE란 실행과 합의를 분리해 서로 독립적으로 작동하도록 하는 아키텍처 접근 방식입니다. 네트워크는 트랜잭션을 실제로 실행하지 않고 그 순서에 합의합니다. 순서가 합의되면 누구나 트랜잭션을 실행해 사실을 확인할 수 있습니다.
이 접근 방식에서 리더는 투표가 아닌 트랜잭션을 실행하지 않고 블록을 구축한 뒤 Turbine을 통해 네트워크에 전파합니다. 검증인도 투표가 아닌 트랜잭션을 실행하지 않고 블록에 투표할 수 있습니다. 합의는 트랜잭션의 순서와 가용성으로만 제한되므로 합의에 필요한 시간과 단계가 크게 줄어듭니다. DBA의 Jon Charbonneau가 작성한 요약을 바꾸어 표현하면, 순서가 사실을 결정하고 실행이 이를 드러냅니다.
이는 현재 합의가 작동하는 방식과 매우 다릅니다. 현재 리더는 트랜잭션을 블록에 담기 전에 실행하고, 모든 검증인은 블록에 투표하기 전에 그 안의 트랜잭션을 재실행합니다.
AE가 가능한 이유는 다음과 같습니다.
- 포크 선택은 프로그램 실행에 의존하지 않습니다.
- 결정론적 상태 전이 함수가 주어지면 올바른 상태는 하나만 계산할 수 있습니다. 결국 모두가 동일한 최종 상태를 계산합니다.
- 유효하지 않은 트랜잭션은 삭제할 수 있습니다. 실패한 트랜잭션 스팸이 네트워크에 주는 비용은 비교적 낮습니다. 트랜잭션 전파에 필요한 Turbine 대역폭과 원장에 트랜잭션을 추가로 저장하는 비용뿐입니다.
- 전용 RPC 서비스 제공업체가 운영하는 시스템처럼 실시간 저지연 전체 상태 계산이 필요한 머신은 이 목적에 맞춰 별도로 프로비저닝할 수 있습니다.
AE에는 많은 잠재적 이점이 있습니다.
Yakovenko는 한발 더 나아가 이렇게 말합니다. “비동기 실행은 사실상 상충 관계가 없는 드문 사례 중 하나입니다.”
- 더 빠른 블록 시간(200밀리초)
- 더 안정적인 블록 시간
- 더 낮은 검증인 요구 사항
- 개선된 사용자 경험(더 빠른 최종성)
- 강화된 검열 저항성
- 트랜잭션을 재정렬할 수 있는 시간 단축
- 블록 간 미사용 용량 이월
- 다중 동시 블록 생산자로 나아갈 경로 제공(뒤에서 자세히 설명합니다)
이 글 전반에서 AE가 이러한 이점을 정확히 어떻게 제공하는지 살펴보겠습니다.
AE를 이해하는 또 다른 방법은 투표 프로그램이 다른 모든 프로그램과 독립적으로 작동하도록 한다는 것입니다. 에포크 내 합의에 필요한 프로그램은 투표 프로그램과 항상 필요한 시스템 프로그램뿐입니다.
Solana의 AE에 관한 논의는 수년간 이어져 왔습니다. Anatoly Yakovenko가 처음 공식 제안한 APEX (Asynchronous Program Execution)는 2022년 4월까지 거슬러 올라갑니다. Solana에서 AE를 실제로 구현하는 구체적인 방법은 여러 SIMD와 글에 나뉘어 있으며, 이 글에서는 대부분 시간순으로 모두 다룹니다. Yakovenko는 의심할 여지 없이 Solana 커뮤니티에서 AE를 가장 적극적으로 공개 지지하고 옹호하는 인물입니다. 인터뷰와 팟캐스트에 출연할 때마다 이 주제를 자주 다룹니다.
용어
AE 논의에서 중요하게 다룰 두 가지 핵심 용어인 Bankless Leaders와 다중 동시 블록 생산자(MCBP)를 간단히 정의하면 이해에 도움이 됩니다. 요약하면 다음과 같습니다.
- Bankless Leader: 트랜잭션을 실행하지 않고 블록을 생성할 수 있는 리더
- 비동기 실행(AE): 합의와 실행의 완전한 분리
- 다중 동시 블록 생산자(MCBP): 이름 그대로 동일한 슬롯에서 여러 블록을 동시에 생산하는 방식
Bankless Leaders는 완전한 AE를 구현하기 위한 필수 단계입니다. 마찬가지로 AE를 성공적으로 구현해야 다중 동시 리더(MCL)라고도 하는 다중 동시 블록 생산자(MCBP)를 이후에 도입할 수 있습니다. AE와 MCBP는 함께 논의되는 경우가 많습니다. Yakovenko는 이 둘을 Solana의 ‘Endgame Architecture’에 반드시 필요한 요소로 지지합니다.
이러한 아키텍처 개선을 연구하는 블록체인은 Solana뿐만이 아닙니다. Monad는 합의와 실행을 별도 스레드로 분리해 AE를 구현할 계획입니다. Ethereum 연구 커뮤니티도 한동안 다중 동시 제안자 도입 가능성을 연구해 왔습니다.
Yakovenko의 제안은 Solana 핵심 프로토콜을 크게 수정해야 하므로 여전히 커뮤니티에서 논쟁 중입니다. 모든 사람이 AE를 최선의 방향으로 보는 것은 아닙니다. Yakovenko가 제안한 Endgame Architecture를 완전히 구현하면 Solana의 운영 방식이 근본적으로 바뀌고 프로토콜의 전반적인 복잡성이 커진다고 해도 과언이 아닙니다.
AE를 더 자세히 살펴보기 전에 현재 Solana에서 실행과 합의가 어떻게 작동하는지 되짚어 봐야 합니다. 특히 이 글의 뒷부분에서 언급할 측면에 주목하겠습니다. 또한 투표 및 비투표 트랜잭션 데이터를 분석해 이후 논의에 필요한 추가 인사이트를 도출합니다. 이미 이러한 기능을 잘 알고 충분히 이해하고 있다면 다음 섹션을 건너뛰어도 됩니다.
동기 실행(현재의 Solana)
실행
Solana는 연속 블록 구축 방식을 사용합니다. 할당된 400밀리초 슬롯 동안 블록을 생성하면서 동적으로 조립하고 스트리밍합니다. 리더는 교체되기 전까지 연속된 슬롯 4개, 즉 1.6초를 배정받습니다. 네트워크가 블록을 수락하려면 리더가 블록 내 각 트랜잭션을 검증하고 실행해야 합니다. 또한 합의에 참여하는 다른 모든 활성 검증인도 블록의 모든 트랜잭션을 다시 검증하고 실행합니다.
리더는 Gulfstream을 통해 QUIC으로 트랜잭션 패킷을 수신합니다. 패킷은 블록 생산을 담당하는 리더의 핵심 로직인 트랜잭션 처리 장치(TPU)로 들어갑니다. TPU가 패킷을 수신하는 개방 포트는 세 개입니다.
- tpu: 토큰 전송, NFT 민팅, 프로그램 명령과 같은 일반 트랜잭션을 처리합니다.
- tpu_vote: 투표 트랜잭션만 처리합니다.
- tpu_forwards: 현재 리더가 모든 트랜잭션을 처리할 수 없을 때 미처리 패킷을 이 포트를 통해 다음 리더에게 전달합니다.
정렬이나 실행을 시작하기 전에 모든 트랜잭션 패킷은 엄격한 유효성 검사와 건전성 검사를 거칩니다. 여기에는 서명 검증, 올바른 서명 수 확인, 중복 트랜잭션 제거가 포함됩니다. 투표 트랜잭션과 비투표 트랜잭션에는 서로 다른 서명 검증 절차가 적용됩니다.
블록 구축은 Banking Stage에서 이루어집니다. bank는 특정 블록 시점의 상태입니다. 트랜잭션은 병렬로 처리되고 원장 엔트리, 즉 서로 충돌하지 않는 트랜잭션 64개의 배치로 패키징됩니다. 모든 트랜잭션에는 읽고 쓸 계정의 전체 목록이 포함됩니다. 이 설계 덕분에 검증인은 각 엔트리에서 서로 충돌하지 않는 트랜잭션만 쉽게 선택해 실행할 수 있습니다. 동일한 계정에 쓰거나(쓰기 2회), 동일한 계정을 읽고 쓰면(읽기 + 쓰기) 트랜잭션이 충돌합니다. 충돌하는 트랜잭션은 서로 다른 엔트리에 들어가 순차적으로 실행되며, 충돌하지 않는 트랜잭션은 병렬로 실행됩니다.
6개의 스레드가 트랜잭션을 병렬 처리합니다. 4개는 비투표 트랜잭션 전용이며 2개는 투표 트랜잭션만 처리합니다. 합의 투표는 Banking Stage에서 우선권이 있는 트랜잭션으로 처리됩니다. 가격은 일률적으로 0.000005 SOL이며, 2,100개의 컴퓨팅 유닛(CU)을 소비하고 Vote Program이 실행합니다.
트랜잭션이 엔트리로 그룹화되면 Solana Virtual Machine(SVM)이 실행합니다. 트랜잭션에 필요한 계정이 잠기며, 트랜잭션이 최근에 생성되었지만 아직 처리되지 않았는지 확인합니다. 계정을 로드하고 트랜잭션 로직을 실행해 계정 상태를 업데이트합니다. 엔트리의 해시는 기록을 위해 Proof of History(PoH) 서비스로 전송됩니다. 성공하면 모든 변경 사항이 bank에 커밋되고 각 계정의 잠금이 해제됩니다.
프로토콜은 블록 내 유효한 트랜잭션의 순서를 규정하지 않습니다. 체인의 최종 상태는 확인된 모든 트랜잭션의 결과입니다. 이 상태는 블록체인 기록에서 항상 결정론적으로 재현할 수 있습니다. 즉, Genesis 또는 스냅샷부터 원장을 재실행하면 됩니다.
각 bank에는 다음 항목의 SHA256 해시인 BankHash가 있습니다.
- 상위
BankHash- 직계 상위 블록의BankHash - 계정
DeltaHash- 현재 블록에서 상태가 변경된 모든 계정의 16진 Merkle 트리 루트 - 서명 수 - 현재 블록의 전체 트랜잭션 서명 수
- 마지막 블록 해시
hashv(&[
parent_bankhash,
accounts_delta_hash,
num_sigs,
blockhash,
])BankHash는 검증인이 각 슬롯에서 투표하는 암호학적 커밋입니다.
합의
검증인이 투표 절차를 진행하려면 세 개의 계정이 필요합니다.
신원 계정
이 시스템 계정은 모든 투표 트랜잭션의 서명자이자 수수료 지불자입니다. 서버 하드웨어에 직접 저장된 핫 키페어입니다. 이름에서 알 수 있듯 이 계정의 공개 키는 검증인의 네트워크 신원으로 사용됩니다. 투표 계정을 생성하려면 신원 계정이 필요합니다.
투표 계정
위임자가 SOL을 위임하는 계정입니다. 주소는 트랜잭션 서명이 아닌 계정 조회에 사용됩니다.
출금 계정
이 계정은 투표 계정에서 자금을 출금할 때 사용합니다. 신원 계정을 변경할 수도 있습니다. 이 계정의 비공개 키는 일반적으로 안전한 다중 서명 또는 콜드 월렛에 저장되며, 검증인 신원 및 투표 권한 키페어와 달라야 합니다.
검증인이 서명한 각 투표(예시)에는 검증인의 공개 키와 투표 대상 블록을 식별하는 해시가 포함됩니다. 검증인이 올바른 투표를 성공적으로 제출하면 크레딧을 받습니다.
네트워크는 다음 블록을 생산하기 전에 모든 검증인이 새 블록에 합의할 때까지 기다리지 않습니다. 따라서 서로 다른 두 블록이 동일한 상위 블록에 연결되어 포크가 생기는 경우가 드물지 않습니다. 검증인은 이러한 포크에 투표하고, 기존 Practical Byzantine Fault Tolerance(pBFT)의 변형인 Tower BFT를 사용해 채택할 포크를 결정해야 합니다.
경쟁하는 포크가 있으면 네트워크는 하나의 포크를 최종 확정하고, 검증인은 폐기된 포크의 블록을 포기합니다. 각 슬롯에는 미리 정해진 리더가 있으며 해당 리더의 블록만 수락됩니다. 하나의 슬롯에 두 블록을 제안할 수 없습니다. 따라서 가능한 포크 수는 리더 교체 슬롯의 경계에서 나타날 수 있는 "있음/없음" 스킵 리스트로 제한됩니다.
검증인이 포크를 선택하면 잠금 시간이 만료될 때까지 해당 포크에 커밋됩니다. 즉, 최소 기간 동안 선택을 유지해야 합니다. 이 메커니즘은 검증인이 포함될 가능성이 가장 높은, 즉 ‘가장 무거운’ 포크를 신중하게 선택해 투표하도록 유도합니다.
3분의 2의 압도적 다수가 블록에 투표하면 낙관적으로 확인된 것으로 간주합니다. 그 위에 블록 31개가 구축되면 최종 확정됩니다. Solana 역사에서 낙관적으로 확인된 블록이 최종 확정되지 않은 사례는 한 번도 없습니다.
포크가 최종 확정되면 블록은 bank의 계정 업데이트와 함께 루트로 확정되고, 상위 블록은 디스크에 기록됩니다. 또한 최종 확정된 bank의 상위 항목이 아닌 이전 bank의 계정 업데이트는 모두 가지치기됩니다.
투표 및 비투표 트랜잭션
지금까지 살펴본 것처럼 Solana의 트랜잭션 실행 절차와 Tower BFT를 통한 합의 도달은 긴밀하게 연결되어 있습니다. 투표 및 비투표 트랜잭션은 동일한 경로를 따릅니다. 엔트리로 묶이고 SVM에서 실행되며 Proof of History(PoH) 스트림에 타임스탬프가 찍힌 뒤 Turbine을 통해 네트워크에 배포됩니다. 투표를 gossip 서비스로만 전파하면 검증인 간에 불일치가 생길 수 있으므로, 이 통합 절차는 검증인이 합의 상태를 일관되게 보도록 합니다. 이러한 불일치가 발생하면 어떤 포크가 올바를 가능성이 높은지에 대해 검증인마다 다른 관점을 가질 수 있습니다. 연속 블록 구축에는 지속적인 투표가 필요하며, 특히 이전 블록을 확인하는 동안 더욱 그렇습니다.
이처럼 함께 묶는 방식의 부수 효과로 Solana의 초당 트랜잭션(TPS) 지표에 관한 논쟁이 생겼습니다. 원시 지표에는 투표 및 비투표 트랜잭션이 모두 포함되지만, 업계의 다른 네트워크 대부분은 그렇지 않습니다. 이에 따라 트랜잭션 용량을 더 정확히 측정하기 위해 투표 트랜잭션을 제외한 “실질 TPS” 지표가 사용되고 있습니다.
최근 Solana 활동을 살펴보면 투표 트랜잭션은 블록당 평균 1,000개에 조금 못 미치며 비투표 트랜잭션보다 많습니다.
투표 트랜잭션은 블록당 전체 컴퓨팅 유닛의 일부만 차지합니다. 블록당 평균 200만 CU를 조금 넘으며 일관되게 210만~230만 CU 범위에 있습니다. 이는 Solana 블록의 컴퓨팅 한도인 4,800만 CU 중 4.5%에 불과합니다.
투표 트랜잭션은 비투표 트랜잭션보다 소요 시간과 컴퓨팅 측면에서 매우 안정적입니다. 이는 이 글의 뒷부분에서 다시 다룰 핵심 사항입니다. 비투표 트랜잭션의 CU는 일정하지 않아 하드웨어를 비효율적으로 소비합니다.
런타임에서 정확한 실행 시간을 보장하기는 어렵습니다. 실행 시간은 정확한 값보다는 근삿값에 가깝기 때문입니다. 블록은 평균적으로 400밀리초 이내를 목표로 하지만 간혹 급증합니다. 이러한 급증은 버그로 간주할 수 있으며, 이를 찾아 수정하는 작업이 지속적으로 이루어져 꾸준히 개선되고 있습니다.
에포크 하나는 432,000개 슬롯으로 구성됩니다. 각 슬롯이 정확히 400밀리초라면 에포크 하나는 정확히 48시간입니다. 그러나 과거 데이터에서 볼 수 있듯 에포크가 이 목표를 충족하는 경우는 거의 없습니다. 지난 1년 동안 에포크는 대체로 2.1~2.3일이 걸렸습니다.
동기 실행에서는 합의에 참여하고 스테이킹한 모든 검증인이 어떤 블록에서든 최악의 실행 시간에 대비해 필요 이상의 리소스를 갖춰야 합니다. 따라서 Solana 검증인의 하드웨어 요구 사항은 비교적 높습니다.
이것으로 현재 Solana의 합의와 실행 방식에 대한 검토를 마칩니다. 주로 이후 논의와 관련된 부분에 초점을 맞췄습니다. 더 포괄적인 내용을 알아보려면 Helius 블로그의 합의, PoH, Turbine, Solana의 작동 방식을 읽어보세요. 다음 섹션에서는 Solana 프로토콜 설계의 향후 변경 사항을 살펴봅니다.
APEX, 2022년
Yakovenko가 처음 공식 제안한 AE는 2022년 4월의 APEX (Asynchronous Program EXecution)입니다. 비교적 짧은 이 제안은 상태를 서로 격리된 두 도메인, 즉 기본 ‘Domain 0’과 Vote Program ‘Domain 1’로 분할하는 개념을 도입하며 합의와 실행을 분리할 실질적인 방법을 제시합니다. 이 구성은 Vote Program을 나머지 bank와 분리합니다.
모든 계정은 하나의 도메인에 속하며, 트랜잭션은 여러 도메인의 계정을 읽거나 쓸 수 없습니다. 이를 시도하는 트랜잭션은 즉시 실패하고 무시됩니다. System Program은 두 도메인에 모두 존재하며, Domain 1에는 이외에 Vote Program만 존재합니다.
특수 시스템 명령(SystemInstruction::MoveDomains)은 투표 계정으로 SOL을 전송하거나 투표 계정에서 SOL을 전송할 수 있도록 계정이 에포크당 한 번 도메인 사이를 이동하는 메커니즘을 제공합니다.
이 제안은 BankHash에 대응하는 ForkHash 개념도 도입합니다. ForkHash 계산은 Domain 1의 Vote Program 명령만 평가하고, BankHash는 Domain 0의 Vote Program이 아닌 모든 명령을 평가합니다. 투표 명령은 ForkHash를 참조합니다. BankHash 계산은 합의 검증인이 투표하는 루트 외부에서 이루어집니다. 이전에는 포크를 선택하고 투표하려면 bank의 모든 트랜잭션을 완전히 평가하고 실행해야 했습니다. 이제 포크 선택에서 평가되는 트랜잭션은 Vote Program 관련 트랜잭션뿐입니다.
이 초기 설계는 많은 질문에 답하지 못했지만, 이후 다시 다룰 격리 도메인의 기본 개념을 확립했습니다.
Bankless Leaders
Bankless Leader는 비투표 트랜잭션을 실행하지 않고 블록을 생산할 수 있는 리더이며, AE 구현을 위한 핵심 전제 조건입니다. Anza 엔지니어 Andrew Fitzgerald는 The Road to Bankless라는 블로그 글에서 같은 Anza 소속 Tao Zhu의 초기 Bankless Leader 제안(SIMD-005, 2022년 12월, 종료)을 발전시켰습니다. Fitzgerald는 Solana에 Bankless Leaders를 도입하는 데 필요한 여러 변경 사항을 설명합니다. 특히 AE 구현을 제한하는 프로토콜 제약을 블록 제약, 엔트리 제약, 트랜잭션 제약이라는 세 가지 핵심 범주로 구분합니다.
현재는 이러한 제약 중 하나라도 위반하면 전체 블록이 유효하지 않게 됩니다. 이 제한을 없애면 Solana 프로토콜에서 AE로 나아갈 길이 열리고 블록 생산과 검증의 유연성이 높아집니다. 다양한 제약을 요약하면 다음과 같습니다.
블록 제약
전체 블록에 적용되는 제약은 다음과 같습니다.
- 블록에는 64개의 틱이 포함되어야 합니다.
- 마지막 블록 엔트리는 틱이어야 합니다.
- 모든 블록 엔트리의 트랜잭션은 블록 한도 내에 있어야 합니다.
엔트리 제약
이 범주의 주요 제약은 엔트리에 충돌하는 트랜잭션을 포함할 수 없다는 것입니다. Fitzgerald는 이 문제를 직접 다룬 SIMD 0083: Relax Entry Constraints를 지난해 11월 제출했습니다. 이 SIMD는 승인되었습니다.
트랜잭션 수준 제약
가장 큰 범주이며 세 가지 하위 범주로 나눌 수 있습니다.
정적 제약
추가 상태를 몰라도 검증할 수 있는 제약입니다. 서명 검증, 트랜잭션 크기, 읽기/쓰기 주소 목록에 기재된 계정 수, 트랜잭션 데이터 형식 준수 여부가 포함됩니다. 즉, 트랜잭션을 역직렬화할 수 있어야 합니다.
트랜잭션 수준 제약의 이 하위 범주는 변경할 필요가 없습니다.
Bank 상태 제약
트랜잭션 서명이 최근 블록에 이미 포함되지 않았는지 확인하는 고유성 검사와 기간 검증이 포함됩니다. 즉, 트랜잭션에는 최근 블록 해시가 포함되어야 합니다.
이 하위 범주를 검증하려면 bank의 최근 상태를 알아야 합니다.
계정 상태 트랜잭션 제약
가장 제한적인 하위 범주입니다. 주소 조회 테이블(ALT) 해석, nonce 검사, 수수료 지불자 검사, 실행 가능 여부 검사가 포함됩니다.
이 하위 범주를 검증하려면 이전 트랜잭션을 알아야 합니다.
Fitzgerald는 현재 공개 상태인 SIMD 0082: Relax Transaction Constraints에서 이 제약 범주의 변경 사항을 공식 제안했습니다.
Fitzgerald가 지적했듯 AE의 핵심 요구 사항은 블록 검증이 계정 상태에 의존해서는 안 된다는 것입니다. 블록을 검증하는 데 이전 트랜잭션의 결과가 필요하면 블록을 비동기식으로 실행할 수 없습니다. 따라서 블록 검증에서 계정 상태에 대한 의존성을 제거해야 합니다. 더욱이 이러한 제약 중 상당수는 불필요하게 엄격합니다.
이러한 제약을 완화해도 현재 트랜잭션 실행 요구 사항은 변경되지 않습니다. 완화된 제약 중 하나를 위반하더라도 블록 검증에서 전체 블록을 거부하지 않는다는 의미일 뿐입니다.
예를 들어 서명자에게 수수료를 지불할 lamport가 부족한 트랜잭션은 현재 프로토콜에서도, 제안된 변경 사항이 적용된 후에도 실행되어서는 안 됩니다. 그러나 제안된 변경 사항이 적용되면 이러한 트랜잭션이 포함되어도 블록의 나머지 부분이 유효하지 않은 것으로 표시되지 않습니다.
전반적으로 Fitzgerald의 작업은 AE를 위한 기반을 마련하도록 Banking Stage를 수정할 실용적인 프레임워크를 제공합니다. 또한 AE를 실현하는 데 필요한 여러 합의 변경 단계를 설명합니다.
APExB 2023: AE와 MCBP의 만남
비동기 프로그램 실행은 Solana의 전체 상태를 롤업으로 바꿀 것입니다.

초기 APEX 설계가 나온 지 약 1년 후인 2023년 3월, Yakovenko는 비동기 프로그램 실행 및 브로드캐스트(Asynchronous Program Execution and Broadcast, APExB) SIMD 0023이라는 더 포괄적이고 구체적인 제안을 내놓았습니다. 이 제안은 AE 구현을 넘어 다중 동시 블록 생성자(Multiple Concurrent Block Producers, MCBP) 통합을 포함한 더 큰 비전을 제시합니다.
이 제안은 리더와 별개의 주체인 ‘빌더’라는 개념을 도입합니다. 빌더는 비투표 트랜잭션으로만 구성된 UserBlocks을 생성하는 스테이킹된 노드입니다. 이 UserBlocks은 Turbine 트리를 통해 전파되며 자체 ‘UserBlockSlots’를 갖습니다. 기본적으로 일반 슬롯당 UserBlockSlots이 2개 있습니다.
빌더는 리더 일정과 독립된 자체 일정을 갖습니다. 여러 빌더가 동시에 UserBlocks를 구축하도록 배정될 수 있습니다. 컴퓨트 유닛은 빌더와 UserBlock 슬롯에 균등하게 배분됩니다. 예를 들어 빌더 2개, UserBlock 슬롯 2개, CU 한도 4,800만인 블록의 UserBlock 슬롯 CU 한도는 1,200만(48/4)입니다.
리더는 기존처럼 리더 일정에 따라 합의 투표 블록을 생성합니다. 동시에 빌더는 사용자 트랜잭션의 UserBlocks을 생성하고 전송합니다. UserBlocks에는 슬롯 번호가 명시되고 빌더의 서명이 포함되어, 리더가 블록의 일부를 조작하거나 제외하지 못하게 합니다. 리더는 Turbine을 통해 UserBlock을 받으면 UserBlock의 해시를 사용해 UserBlockEntry을 생성하고 이를 역사 증명(Proof of History, PoH)에 추가합니다.
검증인은 함께 제공되는 UserBlocks을 아직 받지 못한 블록에 투표할 수 없습니다. 데이터가 보류될 수 있으므로 모든 참여자가 이를 실행할 수 있다고 보장할 방법이 없기 때문입니다. 하지만 UserBlock 트랜잭션을 실행하기 전에 투표만 실행해 리더 블록에 투표할 수 있습니다. 검증인은 가장 무거운 포크에서만 UserBlocks을 실행하며, 투표 시 가장 최근의 BankHash만 제출하면 됩니다. 이는 더 오래된 부모 슬롯에 대한 것일 수도 있습니다. 검증인은 VoteHashes에 투표합니다. 이는 APEX 2022 제안의 ForkHashes과 같은 역할을 하며, 본질적으로 투표 트랜잭션만을 위한 BankHash입니다.
빌더 일정
네트워크 블록당 UserBlock 빌더가 10개라면 각 빌더에는 Turbine 샤드의 10%와 가용 컴퓨트의 10%가 할당됩니다. 빌더는 무작위 방식과 영구 방식이라는 두 절차를 통해 배정됩니다.
각 에포크 경계에서 무작위 빌더는 스테이크 가중 선택 절차에 따라 배정됩니다. 영구 UserBlock 슬롯은 네덜란드식 경매로 할당되며, 가장 많은 SOL을 소각할 의사가 있는 최고 입찰자에게 돌아갑니다.
트랜잭션 우선순위 정렬
각 UserBlock은 리더가 이를 인코딩한 UserBlockSlot 동안 동시에 생성된 것으로 간주됩니다. UserBlockSlot 내 각 UserBlock의 트랜잭션은 실행 전에 우선순위 수수료에 따라 정렬됩니다. 서로 다른 두 블록의 트랜잭션 우선순위가 같다면 리더의 PoH에 먼저 나타나는 UserBlock을 기준으로 정렬됩니다. 즉, 우선순위 수수료가 실행 우선순위를 결정합니다. 중복되거나 유효하지 않은 UserBlocks 트랜잭션은 상태 변경 없이 건너뜁니다. 여러 UserBlockEntries에 같은 트랜잭션이 포함되어 있다면 두 번째 트랜잭션은 건너뜁니다.
위: 시나리오 1에서는 트랜잭션 B가 PoH 스트림에 더 늦게 도착하더라도 우선 처리됩니다. 시나리오 2에서는 트랜잭션 C와 트랜잭션 D의 우선순위 수수료가 같지만 C의 블록이 PoH 스트림에서 더 앞서므로 C가 우선 처리됩니다.
이 설계에서는 빌더가 모든 MEV를 확보합니다. 빌더와 리더가 사용자 트랜잭션 수수료를 어떻게 나눌지는 미결 과제로 남겨 두었습니다.
빌더는 BundleTransactions도 생성할 수 있습니다. 이는 UserBlock 내에서 하나의 순서가 지정된 배치로 실행되도록 설계된 트랜잭션 그룹입니다. 이 트랜잭션에는 우선순위 수수료를 추가할 수도 있습니다. 그러면 현재의 Jito 번들 구현과 유사하게 전체 번들이 하나의 배치로 우선 실행됩니다.
에포크 경계
포크 선택에는 스테이크 가중치가 영향을 미치며, 이 가중치는 각 에포크 경계에서 업데이트됩니다. 이는 설계에 중요한 영향을 줍니다. 검증인이 에포크 경계를 넘어 계속 투표하려면 먼저 비투표 트랜잭션 실행을 따라잡아야 합니다.
하지만 Yakovenko는 다음과 같이 설명합니다. “전체 CU 한도가 동기식 실행에 맞춰 설정되어 있으므로 노드가 그렇게까지 뒤처질 수는 없습니다. 하지만 비동기 실행 옵션이 있으면 훨씬 쉽게 따라잡을 수 있습니다. 네트워크를 처리하지 않는 원시 원장 처리는 20~30배 더 빠릅니다.”
고려 사항
빌더가 여러 개면 리소스 관리가 더 복잡해집니다. 클라이언트는 가장 가까운 빌더를 선택할 수 있지만 UserBlock마다 우선순위 수수료가 다를 수 있습니다. 사용자는 트랜잭션을 보낼 때 어떤 블록이 포화 상태가 될지 알기 어렵습니다.
장점 중 하나는 일정의 예측 가능성입니다. 애플리케이션은 운영에 필요한 블록 대역폭 비율에 입찰하고, 최종적으로 체인에 정산될 것을 보장하는 전용 시퀀서를 만들 수 있습니다. 무작위 UserBlock 빌더를 포함하는 것도 중요합니다. 영구 블록 빌더가 트랜잭션을 검열하고 최종 반영을 막는 일을 방지하기 때문입니다.
2024년 Solana의 APE
Yakovenko는 올해 6월에 발표한 비동기 프로그램 실행(Asynchronous Program Execution, APE)이라는 X 아티클에서 2022년 APEX 제안에 처음 설명된 실행 도메인 개념을 더 구체화했습니다.
원래 Domain 0과 Domain 1이었던 실행 도메인은 이제 투표 실행 도메인(Vote Execution Domain, VED)과 사용자 실행 도메인(User Execution Domain, UED)으로 이름이 바뀌었습니다. 목적은 그대로입니다. 합의 투표와 트랜잭션 실행을 완전히 분리하는 것입니다.
실행 도메인은 다른 집합과 독립적으로 실행되는 별개의 프로그램 집합과 이들이 상호작용하는 키 및 값으로 정의됩니다.
- 실행 도메인은 서로 다른 스레드와 코어에서 실행될 수 있으며 물리적으로 분리된 머신에서 각기 다른 시점에 완료될 수 있습니다
- 실행 도메인 A는 실행 도메인 B의 어떤 값도 읽거나 쓸 수 없습니다
- 도메인은 어느 한 도메인이 실행되는 동안 일관성을 유지하는 상태를 공유할 수 있습니다
- 수수료 납부자가 트랜잭션이 실행될 도메인을 결정합니다
- 도메인 간 상태를 동기화하고 키와 값의 이동을 지원하는 프로토콜이 필요합니다
투표 실행 도메인(VED) 구성 요소
SystemProgram: 전송에 사용됩니다VoteProgram: 투표를 위한 핵심 프로그램입니다. 이 프로그램은 정적이며 UED와 VED 모두에 있어야 합니다- VoteProgram Sysvars: 투표에 사용되는 변수입니다
- 투표 권한: 투표 권한이 있는 계정입니다
- 투표 수수료 납부자: 투표 관련 수수료를 납부하는 계정입니다
- 수수료 납부자 자금 제공 계정: SOL을 VED로 옮기는 데 사용할 수 있는 업데이트 가능한 계정입니다
VED에 속하지 않는 계정은 UED의 일부로 간주됩니다.
투표 계정은 활성화해야 합니다. 활성화된 다음 에포크에 VED에 포함됩니다. 비활성화되면 다음 에포크에 VED에서 제거됩니다. VED 안팎으로 자금을 이동하는 공식 절차도 정의되어 있습니다.
계정은 Linux 파일 권한(R 읽기, W 쓰기, X 실행)과 유사한 규칙에 따라 자신이 매핑된 실행 도메인을 추적합니다. 유효한 매핑은 4가지입니다.
VED와 UED 사이를 이동할 수 있는 것은 Vote Program 및 System Program 계정뿐입니다. System Program은 System 계정과 Vote Program 계정을 VED 안팎으로 이동하는 인터페이스를 제공합니다. 이 접근 방식에서는 명시적인 FeePayerFunding 계정이 필요하지 않습니다. 어떤 System 계정이든 VED와 UED 사이에 다시 매핑하여 도메인 간 자금을 이동할 수 있습니다.
리더는 자신이 생성한 블록에 대해 VED 도메인만 실행합니다. 따라서 UED 수수료 납부자의 상태에 관한 정보가 일부만 있거나 불완전할 수 있습니다. 블록을 받은 검증인은 먼저 VED 트랜잭션을 실행합니다. 그 결과로 나온 VED 상태를 사용해 VED Hash를 계산하고, 검증인은 이 해시를 사용해 투표합니다.
UED 재실행은 수수료 납부자가 유효하지 않은 트랜잭션을 건너뜁니다. UED 상태 업데이트는 UED Hash라고도 하는 Bankhash를 계산합니다. 검증인의 3분의 1 이상이 서로 다른 UED Hash를 제출하면 모든 노드는 중단하고 운영자에게 경고해야 합니다.
새 기능 활성화
이제 합의와 실행이 분리되었으므로 VoteProgram은 VED 투표가 에포크 경계를 넘을 수 있는 상황을 처리하도록 설계해야 합니다. 이 경우 새로운 기능 활성화 시점이 UED에서 VoteProgram과 상호작용하는 트랜잭션의 활성화 시점과 달라질 수 있습니다.
Endgame Architecture 2024
애플리케이션과 핵심 개발자가 다양한 만큼 매년 하나의 주요 프로토콜 변경을 계획하는 것은 충분히 가치가 있습니다. 하나를 선택해야 한다면 저는 비동기 실행에 투표하겠습니다.

2024년 초 Yakovenko는 Solana가 블록 시간이 120밀리초인 10,000개 노드 네트워크로 발전한다는 대담한 미래상을 담은 문서인 Endgame Architecture를 발표했습니다. 이 문서는 앞선 설계 제안을 재확인하고 확장하면서, 이 야심 찬 비전을 실현할 경로로 AE 도입을 강조합니다.
Bankless 리더
요약하면 다음과 같습니다.
● 리더는 수수료 납부 계정 잔액의 캐시를 유지합니다
● 수수료 납부자가 시스템 전송의 출처인 쓰기 가능 계정으로 사용되거나 System Program과 함께 쓰기 가능 계정으로 다른 프로그램에 전달되면 수수료 납부 잔액은 0으로 설정됩니다
● 블록은 로컬 수수료 우선순위 정렬에 따라 가득 찰 때까지 선언된 CU를 기준으로 채워지며, 트랜잭션 수수료는 수수료 납부자 잔액 캐시에서 차감됩니다
● 수수료 납부 계정 잔액 캐시는 BankHash 계산을 통해 보충됩니다
리더는 처음에 여러 풀 노드 또는 RPC를 쿼리하여 수수료 납부자의 계정 잔액을 얻을 수 있습니다. 드물게 노드가 잘못된 데이터를 제공하더라도 합의 실패가 아니라 트랜잭션 실패로 이어집니다. 수수료 납부자 잔액이 오래되었다면 합의에 영향을 주지 않으면서 블록 내 스팸을 유발할 수 있습니다. 실패한 트랜잭션 스팸의 비용은 비교적 낮습니다. 주로 트랜잭션 전파에 필요한 Turbine 대역폭과 원장에서 차지하는 추가 저장 공간이 비용의 대부분입니다. 또한 Temporal과 Firedancer 팀 모두 유효하지 않은 트랜잭션 필터링, 중복 제거, 읽기/쓰기 잠금 충돌로 실패가 확실한 트랜잭션 차단 같은 메커니즘을 구현하여 스팸이 블록에 도달하기 전에 완화하는 도구를 적극적으로 개발하고 있습니다.
이 문제는 쉽게 감지하고 모니터링할 수 있습니다. 운영자는 리더 성능을 추적하고 블록의 스팸 수준을 평가할 수 있습니다. 따라서 문제를 신속히 해결하고 필요하면 대체 데이터 소스로 전환할 수 있습니다. 검증인은 수익을 극대화하려는 동기가 있으므로 수수료 납부자 계정 잔액의 정확한 캐시를 유지할 유인이 있습니다.
고정 크기 소위원회
10,000개 노드로 구성된 대규모 네트워크 전반에 AE를 구현하려면 고정 크기의 순환식 투표 위원회를 도입해야 합니다. 이는 합의 메커니즘의 중대한 변화입니다. 이 변경으로 스테이킹된 전체 노드 수와 관계없이 합의 투표의 리소스 비용을 안정적으로 유지할 수 있습니다. Yakovenko는 200개 또는 400개 노드로 구성된 순환식 위원회면 충분하다고 제안합니다. 이 구성에서 검증인은 쿼럼, 스테이크 가중치, 투표 계정 잔액만 포함하는 최소한의 상태 요구 사항으로 합의 투표에 참여할 수 있습니다. 메모리 사용량이 적으며, 해당 스냅샷 파일은 쉽게 배포하고 재시작 시 다시 초기화할 수 있습니다.
진짜 아이러니는 비동기 실행 이후 Solana 합의 검증인을 FPGA에서 실행할 수 있다는 점일 겁니다. 1만 개 투표 계정을 위한 Turbine과 tower는 FPGA에 충분히 들어갑니다. 추적할 상태는 약 4MB 정도일 것입니다.

비잔틴 장애 허용(Byzantine Fault-Tolerant, BFT) 프로토콜에서 고정 크기의 순환식 소위원회를 사용하는 방식은 이미 확립된 개념이며 Tron 같은 유사 네트워크가 프로덕션에서 구현했습니다. 이 방식은 합의를 처리할 소규모 위원회를 선택하고 그 결과를 전체 복제본 네트워크에 전달하는 위원회 샘플링 접근 방식에 기반합니다.
Solana의 초기 접근 방식으로는 투표에 일정한 수의 CU를 할당하고, 각 에포크마다 스테이크 순위가 높아 이 CU에 포함되는 검증인을 선택해 포크 선택을 추적하게 할 수 있습니다. 다른 검증인도 계속 투표하고 보상을 받지만 그들의 투표는 포크 선택에 반영되지 않습니다.
투표 계정
● 투표 계정에는 두 에포크의 투표 비용을 충당할 만큼 충분한 SOL이 있어야 합니다
● 투표 트랜잭션은 단순 투표여야 합니다
● 잔액이 한 에포크의 투표 비용을 초과하면 투표 계정에서 SOL을 인출할 수 있습니다
● 모든 lamport를 제거하려면 Vote CLOSE 명령이 전체 에포크 하나가 지나도록 요구해야 합니다. 투표 계정은 첫 번째 에포크에 CLOSE 대상으로 표시되지만 두 번째 에포크에만 CLOSE될 수 있습니다
CLOSE을 사용하면 모든 SOL을 인출하고 투표 계정을 삭제할 수 있습니다. 계정이 CLOSE 대상으로 표시되면 삭제만 가능하며 다시 열 수 없습니다
● 투표에는 일반 BankHash 대신 VoteBankHash이 포함됩니다
BankHashes
검증인은 다른 모든 트랜잭션을 무시하고 현재 BankHash과 같은 형식으로 단순 투표 트랜잭션의 VoteBankHash을 계산합니다. 이 VoteBankHashes에는 전체 BankHash이 아니라 이전 VoteBankHash이 포함됩니다.
3분의 2의 압도적 다수가 낙관적으로 확인한 블록에 대해 검증인은 UserBankHash 계산도 시작합니다. 여기에는 VoteBankHash에서 이미 반영한 항목을 제외한 모든 상태 전환이 포함됩니다.
각 슬롯에서는 VoteBankHash, UserBankHash, 이전 BankHash을 결합해 BankHash을 계산합니다. 상위 99.5%의 검증인은 100번째 슬롯마다 투표의 일부로 이 BankHash을 제출합니다. 또한 일부 노드는 gossip 네트워크를 통해 BankHash을 브로드캐스트하여 비결정성이 감지되지 않았음을 알릴 수 있습니다.
검증인의 3분의 2 미만이 전체 BankHash을 제출한다고 가정해 보겠습니다. 이 경우 리더는 사용자 트랜잭션과 쓰기 가능 계정을 위한 블록 공간을 50% 줄여 재실행 시간을 과도하게 늘릴 수 있는 악용을 방지할 수 있습니다.
다음 쿼럼을 구성하기 위해 상태는 에포크마다 한 번만 계산하면 되므로 합의 노드와 리더가 동기화된 상태를 유지할 수 있습니다. 실행은 합의 노드와 별도의 머신에서 집계하고 배치 처리할 수 있습니다. 동기식 실행이 필요한 사용자(대부분의 애플리케이션과 RPC)는 더 넓은 네트워크를 기다리지 않고 모든 상태 전환을 실시간으로 처리하도록 전용 하드웨어 리소스를 배정할 수 있습니다.
고려 사항
사용자에게 실시간 상태 데이터를 제공하는 RPC 제공자에게는 로컬에서 계산한 상태가 더 넓은 네트워크에서 계산한 상태와 일치하는지 검증할 서명이 없습니다. 하지만 포크가 확정되면 네트워크는 결정론적으로 계산할 수 있는 하나의 올바른 정식 상태로 수렴합니다. 런타임 버그나 데이터 손상 위험을 최소화하려면 정확한 상태 데이터를 제공하려는 운영자가 여러 노드를 운영해야 합니다. 상태 실행에서 불일치가 감지되면 운영을 즉시 중단해야 합니다.
또한 사용자는 BankHash을 단언하거나 중단을 트리거하는 트랜잭션을 제출할 수 있습니다. 네트워크는 계산된 BankHash이 RPC 제공자가 사용자에게 제공한 값과 일치할 때만 이 트랜잭션을 처리합니다.
결론
Yakovenko는 AE 도입을 통해 블록 시간을 120밀리초로 단축하고 노드 수를 대폭 늘리는 Solana의 대담한 미래를 그리고 있습니다. 하지만 이 미래를 실현하려면 각기 다른 클라이언트 팀과 더 넓은 Solana 개발자 커뮤니티를 이 비전에 결집하고, 이러한 변경을 구현하는 데 따르는 중대한 엔지니어링 과제를 해결해야 합니다.
주요 커뮤니티 구성원들은 제안된 여러 변경 사항에 우려를 표했습니다. Firedancer 팀의 Richard Patel은 *“블록 생성자를 위한 비동기 실행 구현은 상당히 복잡하며 큰 위험을 수반합니다”*라고 경고했습니다.
Jito Labs의 Zano Sherwani는 AE를 지지하지만 MCBP에 대해서는 *“다중 동시 제안자는 해결할 문제를 찾고 있는 끔찍한 솔루션입니다. 프로토콜에 추가되는 복잡성은 그것이 해결하려는 문제가 무엇이든 간에 정당화되지 않습니다”*라고 비판했습니다.
최근 팟캐스트에서 여러 블록 빌더에 관한 질문을 받은 Helius의 Mert Mumtaz는 *“아직 완전히 확신하지는 못했습니다”*라고 답했습니다.
AE가 상당한 성능 향상을 제공할 잠재력이 있다는 점은 분명합니다. 블록 시간이 짧아지면 확인 속도가 빨라지고 사용자 경험이 개선됩니다. 동시에 검열 저항성이 높아지고 트랜잭션 순서를 재조정할 수 있는 시간도 줄어듭니다.
하지만 프로토콜 복잡성이 증가할 가능성과 RPC 제공자에 대한 의존도가 커지는 문제를 포함한 단점도 신중하게 평가해야 합니다. 특정 상황에서는 이러한 제공자만이 체인 상태를 최신 상태로 실시간 파악할 수 있을 수 있습니다.
AE는 Solana의 주요 업그레이드입니다. 이 글을 통해 AE가 가져올 흥미로운 변화를 Solana 개발자 커뮤니티에 알리고, 이 중요한 주제에 관해 더 깊이 있는 논의가 이루어지기를 바랍니다.
이 글의 이전 버전을 검토해 주신 0xIchigo와 Anatoly Yakovenko께 깊이 감사드립니다.
추가 자료
- Solana의 합의 - Helius 블로그
- Solana 개요 - Helius 블로그
- Bankless로 가는 길 - A. Fitzgerald
- 비동기 프로그램 실행(APE) - A.Yakovenko
- Endgame Architecture - A.Yakovenko
- 비동기 프로그램 실행 및 브로드캐스트(APExB) SIMD 0023
관련 아티클
Helius 구독하기
최신 Solana 개발 소식을 확인하고 새 게시물 알림을 받아보세요


