
Solana 중단의 전체 역사: 원인, 해결책, 교훈
삐, 삐, 삐. 삐, 삐, 삐.
날카로운 휴대전화 알림음이 Steven을 꿈속에서 갑자기 끌어냅니다. 어둠 속에서 화면이 밝게 빛나며 침대 옆 탁자 위에서 거세게 진동합니다. 삐, 삐, 삐. 그는 신음하며 잠이 덜 깬 채 눈을 비비고 휴대전화를 집어 듭니다. 눈을 가늘게 뜨고 메시지를 확인하는 순간 가슴이 철렁합니다. 노드가 다운됐습니다. 그는 망설임 없이 침대에서 뛰쳐나와 옷도 제대로 입지 못한 채 쏟아지는 메시지를 보며 휴대전화 잠금을 풀려고 애씁니다. 그리고 상황을 깨닫습니다. 전체 클러스터가 다운됐습니다.
바로 이 순간, 전 세계 여러 도시와 시간대에 흩어진 수백 명의 노드 운영자도 휴대전화를 보며 같은 사실을 깨닫습니다. 그들이 두려워하던 순간, 중단 사태가 발생한 것입니다.
소개
모든 분산 시스템과 마찬가지로 Solana에서도 단일 구현 결함이나 잘 드러나지 않는 엣지 케이스가 네트워크 전체 장애로 이어질 수 있습니다. 중단은 운영에 지장을 주지만 복잡한 분산 인프라를 유지하는 과정에서 피할 수 없는 일입니다. 탈중앙화 블록체인뿐 아니라 중앙화 거래소, Amazon이나 Microsoft 같은 대형 클라우드 서비스 제공업체도 마찬가지입니다.
중요한 것은 장애가 발생하는지 여부가 아니라 언제 발생하며, 네트워크가 미래의 사고에 적응하고 더 견고해지도록 어떻게 발전하는지입니다. 엄격한 시뮬레이션 테스트, 인센티브 기반 테스트넷, 활발한 버그 바운티 프로그램을 운영하더라도 아무리 잘 설계된 시스템도 가능한 모든 장애 유형을 예측할 수는 없습니다. 가장 값진 교훈은 실제 운영에서 나옵니다.
지난 5년 동안 Solana에서는 총 7건의 중단 사고가 발생했습니다. 이 중 5건은 클라이언트 버그, 2건은 대량의 스팸 트랜잭션을 네트워크가 처리하지 못해 발생했습니다. 초기 Solana에는 우선순위 수수료와 로컬 수수료 시장 같은 핵심 혼잡 관리 메커니즘이 없었습니다. 이후 이러한 메커니즘은 네트워크 부하를 완화하는 데 필수적인 것으로 확인됐습니다. 이런 장치가 없었던 탓에 네트워크가 사실상 스팸을 장려하면서 2022년 내내 성능 저하와 혼잡이 장기간 이어졌습니다.
이 글에서는 각 Solana 중단 사고를 자세히 분석하고 근본 원인, 촉발 요인, 해결 조치를 살펴봅니다. 또한 네트워크 재시작과 버그 보고의 핵심 요소, 활성과 안전성 실패의 기본 개념도 다룹니다. 순서대로 읽는 것이 가장 좋지만 각 섹션은 독립적으로 이해할 수 있게 구성했습니다. 관심 있는 주제나 중단 사고로 바로 이동해도 됩니다.
활성과 안전성
Brewer의 정리로도 알려진 CAP 정리에 따르면 분산 시스템은 다음 세 가지 속성 중 두 가지만 충족할 수 있습니다.
- 일관성 - 모든 읽기 작업에 이전의 모든 쓰기 작업이 반영됩니다.
- 가용성 - 모든 요청이 응답을 받습니다.
- 파티션 내성 - 네트워크 파티션이 발생해도 시스템이 계속 작동합니다.
블록체인에는 파티션 내성이 필수입니다. 네트워크 장애는 피할 수 없기 때문입니다. 따라서 AP(가용성 + 파티션 내성)와 CP(일관성 + 파티션 내성) 중 하나를 선택해야 합니다. 최종 확정이 빠른 대부분의 PoS 체인처럼 Solana는 가용성보다 일관성을 우선하는 CP 시스템입니다. 오래된 데이터를 제공하거나 안전하지 않은 쓰기를 허용하는 대신 심각한 장애가 발생하면 중단됩니다. 이로 인해 노드 소프트웨어가 수동 개입 없이는 복구할 수 없는 상태에 빠질 수 있지만, 사용자 자금의 안전은 보장됩니다.
활성 실패: 검증인 중단, 네트워크 파티션 또는 합의 정체로 인해 블록체인의 진행이 멈추고 트랜잭션을 확정하거나 블록을 생성할 수 없는 상태입니다. CAP 정리에서는 가용성 상실에 해당합니다.
안전성 실패: 블록체인의 최종 확정 상태가 부적절하게 변경되거나 포크되는 상태입니다. 합의 버그나 악의적 공격으로 인해 서로 충돌하는 기록 또는 이중 지불이 발생할 수 있습니다. CAP 정리에서는 일관성 상실에 해당합니다.
Solana는 활성보다 안전성을 우선합니다. 따라서 네트워크가 극심한 부하를 받거나 합의에 실패하면 상태 손상을 감수하는 대신 중단됩니다. 중단은 운영에 지장을 주고 애플리케이션, 사용자, 검증인에게 영향을 줄 수 있지만, 일관되지 않거나 손상된 원장이 초래할 치명적인 결과보다는 낫습니다.
네트워크 재시작
Solana 네트워크를 재시작하려면 마지막으로 낙관적 확정이 이뤄진 블록 슬롯을 찾고, 해당 슬롯의 신뢰할 수 있는 로컬 상태 스냅샷에서 노드를 다시 시작해야 합니다. 재시작 슬롯은 온체인에서 결정되지 않으므로 검증인 운영자는 오프체인 합의를 통해 안전한 롤백 지점에 동의해야 합니다. 이 조율은 Solana Tech Discord의 #mb-validators 채널에서 공개적으로 진행되며 전문 검증인 운영자가 실시간으로 소통합니다. 대부분의 운영자는 블록 생성이 중단되는 즉시 알리는 자동 경보 시스템을 갖추고 있어 빠르게 대응할 수 있습니다.
올바른 재시작 슬롯에 합의하면 운영자는 ledger 도구로 새 로컬 스냅샷을 생성하고 검증인을 재시작한 뒤 전체 스테이크의 최소 80%가 온라인으로 복귀할 때까지 기다립니다. 그 후에야 네트워크가 블록 생성과 검증을 재개합니다. 클러스터 재시작 시 오프라인 스테이크가 최대 20%인지 확인하면 노드가 포크되거나 재시작 직후 다시 오프라인 상태가 되더라도 온라인 상태를 유지할 충분한 안전 여유를 확보할 수 있습니다.
버그 보고
버그 바운티 프로그램은 소프트웨어 취약점을 발견하고 보고한 보안 연구원에게 보상을 제공합니다. 악용되기 전에 버그를 발견하도록 선제적으로 장려하는 핵심 방어선입니다. Agave 클라이언트에서 잠재적 취약점을 발견한 보안 연구원과 개발자는 적절한 보안 채널을 통해 보고하는 것이 좋습니다. 자세한 공개 지침은 Agave GitHub 저장소에서 확인할 수 있습니다.
유효한 심각한 취약점 보고에는 심각도에 따라 다음과 같은 보상이 제공됩니다.
- 자금 손실: 최대 25,000 SOL
- 합의 또는 안전성 위반: 최대 12,500 SOL
- 활성 또는 가용성 상실: 최대 5,000 SOL
또한 FireDancer 클라이언트에는 Immunefi에서 운영하는 별도의 버그 바운티 프로그램이 있으며, 심각한 취약점을 발견하면 최대 500,000 USDC를 보상합니다.
중단 사례
다음 섹션에서는 2020년 3월 16일 메인넷 베타 출시 이후 발생한 Solana의 중단과 성능 저하 기간을 시간순으로 자세히 분석합니다. 주요 사고와 근본 원인, 이후 이뤄진 네트워크 개선을 살펴보며 Solana가 시간이 지나면서 안정성과 복원력을 어떻게 강화했는지 알아봅니다.
Turbine 버그: 2020년 12월
중단 시간: 약 6시간
근본 문제: 블록 전파 버그
해결책:
- 슬롯 번호 대신 해시로 블록 추적
- 장애를 더 일찍 감지할 수 있도록 Turbine의 관련 부분 수정
- 처음 감지된 장애를 gossip을 통해 모든 검증인에게 전파
이 중단은 이전에 알려진 블록 복구 및 코드 처리 문제에서 비롯됐습니다. Solana의 블록 전파 메커니즘인 Turbine의 확인되지 않은 버그가 이를 촉발했습니다. 한 검증인이 동일한 슬롯에 서로 다른 두 블록을 전송해 별도의 두 파티션(A와 B)에 전파했고, 세 번째 파티션이 이 불일치를 독립적으로 감지하면서 장애가 발생했습니다.
각 파티션은 소수의 스테이크만 보유했기 때문에 어느 쪽도 체인을 진행하는 데 필요한 압도적 다수의 합의에 도달할 수 없었습니다. 근본 원인은 Solana 내부 데이터 구조가 블록과 계산된 상태를 추적하는 방식에 있었습니다. 시스템은 해당 슬롯의 상태와 블록을 참조할 때 Proof of History(PoH) 슬롯 번호(u64 식별자)를 사용했습니다. 네트워크가 파티션으로 나뉘자 노드가 블록 A와 B를 동일한 것으로 잘못 해석해 정상적인 복구와 블록 동기화가 불가능해졌습니다.
각 파티션은 상대방도 같은 블록을 보유한다고 가정했고, 이로 인해 근본적인 충돌이 발생했습니다.
- 블록 A를 보유한 노드는 블록 B에서 파생된 포크를 거부했습니다.
- 블록 B를 보유한 노드는 블록 A에서 파생된 포크를 거부했습니다.
파티션마다 상태 전환이 달랐기 때문에 검증인은 포크를 복구하거나 조정할 수 없었고 최종 확정에 도달하지 못했습니다.
해결책은 서비스가 슬롯 번호 대신 해시로 블록을 추적하도록 허용하는 것이었습니다. 동일한 슬롯의 여러 블록이 파티션을 만들더라도 서로 다른 슬롯을 차지하는 블록으로 생긴 파티션과 똑같이 처리합니다. 노드는 가능한 모든 포크를 복구할 수 있고, 합의 메커니즘은 파티션을 해소할 수 있습니다.
버그가 중단의 최초 원인이었지만, 중단 시간 대부분은 충분한 스테이크 비중이 온라인으로 돌아오기를 기다리는 데 쓰였습니다. Solana가 블록 생성을 재개하려면 스테이크 참여율이 최소 80%에 도달해야 하기 때문입니다.
Grape Protocol IDO: 2021년 9월
중단 시간: 17시간
근본 문제: 봇 트랜잭션으로 인한 메모리 오버플로
해결책:
- 프로그램의 쓰기 잠금 무시
- 트랜잭션 전달 속도 제한
- 설정 가능한 RPC 재시도 동작
- TPU 투표 트랜잭션 우선 처리
2021년 9월 14일, Grape Protocol이 크라우드펀딩 플랫폼 Raydium AcceleRaytor에서 온체인 초기 DEX 공개(IDO)를 시작한 후 Solana 네트워크가 크게 정체됐습니다. IDO 시작 후 12분 만에 봇이 전송한 전례 없는 규모의 트랜잭션이 네트워크를 압도했고, 루트 슬롯 생성이 중단됐습니다. 이 봇들은 사실상 분산 서비스 거부(DDoS) 공격을 실행해 네트워크 용량을 초과하는 트랜잭션 부하를 일으켰습니다.
혼잡이 최고조에 달했을 때의 상황은 다음과 같습니다.
- 일부 검증인은 초당 300,000건이 넘는 트랜잭션을 수신했습니다.
- 원시 트랜잭션 데이터는 초당 120,000개 패킷, 1Gbps를 초과했습니다.
- 트래픽이 네트워크 인터페이스의 물리적 한계를 넘는 경우도 있어 검증인에게 도달하기도 전에 스위치 포트에서 패킷이 손실됐습니다.
봇 중 하나는 글로벌 SPL 토큰 프로그램과 현재는 운영이 중단된 Serum DEX 프로그램을 포함한 핵심 계정 18개에 쓰기 잠금을 설정하도록 트랜잭션을 구성했습니다. 이 계정과 상호작용하는 모든 트랜잭션이 차단되면서 Solana의 병렬 처리 능력이 크게 저하됐습니다. 트랜잭션을 독립적으로 실행하는 대신 네트워크에 병목이 생겨 트랜잭션을 순차적으로 처리했고, 혼잡은 더욱 심해졌습니다.
프로그램의 쓰기 잠금을 무시하는 수정 사항은 이미 개발이 끝나 출시를 앞두고 있었습니다. 이후 네트워크 재시작 과정에서 이 업그레이드를 적용해 해당 공격 경로를 영구적으로 제거했습니다.
IDO 이벤트 동안 검증인은 봇이 전송한 대량의 트랜잭션을 수신한 뒤 초과 트랜잭션을 다음 리더에게 전달해 혼잡을 증폭시켰습니다. 네트워크 재시작 시 트랜잭션 전달에 속도 제한을 도입해 향후 트랜잭션 폭주가 리더를 압도하지 못하도록 했습니다.
Solana의 RPC 노드는 실패한 트랜잭션을 자동으로 재시도합니다. 안정성을 높이기 위한 기능입니다. 그러나 극심한 혼잡 상황에서는 이 재시도 메커니즘이 트랜잭션 폭주를 악화시켰습니다. 오래된 트랜잭션이 계속 순환하면서 네트워크 복구를 방해했습니다. Solana 1.8에서는 설정 가능한 RPC 재시도 동작을 도입했습니다. 애플리케이션은 더 짧은 만료 시간과 지수 백오프 전략으로 재시도를 최적화할 수 있게 됐습니다.
혼잡이 심해지자 Solana 리더는 합의 유지에 필수적인 투표 트랜잭션을 포함하지 못했습니다. 확정된 투표가 부족해지면서 합의가 정체됐고 새로운 루트 블록 생성도 중단됐습니다. 이후 Solana 클라이언트 버전에는 투표 트랜잭션을 우선 처리하는 메커니즘이 도입돼 향후 유사한 상황에서 일반 트랜잭션에 밀리지 않도록 했습니다.
두 번째 버그: 정수 오버플로
네트워크를 재시작하는 동안 두 번째 문제가 나타났습니다. 검증인은 활성 스테이크 수량이 심하게 변동한다고 보고했습니다. 이 문제는 스테이크 비율에 100을 잘못 곱해 가능한 최대값을 초과한 버그에서 비롯됐습니다. 인플레이션 메커니즘이 너무 많은 신규 SOL 토큰을 생성해 64비트 부호 없는 정수에서 오버플로가 발생했습니다. 이 버그는 두 번째 재시작 전에 신속히 발견되어 수정됐습니다.
극심한 혼잡: 2022년 1월
중단 시간: 없음
근본 원인: 과도한 중복 트랜잭션 부분적 해결책:
- Solana 1.8.12 및 1.8.14 출시
- SigVerify 중복 제거 최적화
- 실행기 캐시 성능 개선
2022년 1월 6일부터 12일까지 Solana 메인넷에서 심각한 네트워크 혼잡이 발생해 성능이 저하되고 부분적인 중단이 일어났습니다. 봇이 과도한 중복 트랜잭션을 스팸으로 전송하면서 네트워크 용량이 크게 줄어든 것이 원인이었습니다. 블록 처리 시간이 예상보다 길어져 다음 리더가 포크됐고 처리량은 더 감소했습니다. 정점에는 트랜잭션 성공률이 최대 70%까지 떨어졌습니다. 클라이언트는 갈수록 복잡해지고 연산량이 많아지는 네트워크 트랜잭션을 처리하는 데 어려움을 겪었고, 수요 대응 역량의 한계가 드러났습니다.
1월 21일부터 23일까지도 혼잡이 지속되며 추가적인 불안정이 발생했습니다. 1월 22일에는 스팸으로 전송된 일괄 RPC 호출이 시스템을 압도하면서 악용을 견디지 못한 공개 RPC 엔드포인트(https://api.mainnet-beta.solana.com)가 오프라인 상태가 됐습니다.
이 문제를 해결하기 위해 Solana 1.8.12 릴리스는 프로그램 캐시 고갈 문제를 집중적으로 다뤘고, 1.8.14 버전에서는 Sysvar 캐시, SigVerify 폐기, SigVerify 중복 제거 기능을 개선했습니다.
Candy Machine 스팸: 2022년 4월/5월
중단 시간: 8시간
근본 문제: 봇 계정의 트랜잭션 스팸
해결책:
- Candy Machine 프로그램에 봇 세금 도입
- Solana v1.10의 메모리 개선
2022년 4월 30일, Solana의 트랜잭션 요청이 전례 없이 급증했습니다. 일부 노드는 초당 600만 건의 요청을 받아 노드당 100Gbps가 넘는 트래픽이 발생했다고 보고했습니다. Metaplex Candy Machine 프로그램에서 새로 발행되는 NFT를 확보하려는 봇이 이러한 급증을 일으켰습니다. 이 발행 메커니즘은 선착순으로 작동했기 때문에 네트워크에 트랜잭션을 쏟아붓고 발행 기회를 차지하려는 강력한 경제적 유인이 생겼습니다.
트랜잭션 양이 급증하자 검증인의 메모리가 고갈되어 작동이 중단됐고, 결국 합의가 정체됐습니다. 투표 처리량이 부족해 이전 블록을 최종 확정하지 못했고, 포기된 포크도 정리할 수 없었습니다. 검증인은 평가해야 할 수많은 포크에 압도됐습니다. 재시작 후에도 처리 용량을 초과해 네트워크 복구에 수동 개입이 필요했습니다.
이번 중단은 2021년 9월 사고와 비슷했지만 Solana의 복원력은 향상됐습니다. 이전 중단보다 트랜잭션 요청이 10,000% 많았음에도 네트워크는 훨씬 오래 작동했습니다. 과거의 확장성 문제에 대응해 검증인 커뮤니티가 이룬 개선이 효과를 보인 것입니다.
표준 스냅샷에 합의한 후 네트워크를 재시작하는 데 1.5시간도 걸리지 않았습니다. Solana v1.10에는 합의가 느려지거나 정체된 상황에서 노드가 더 오래 버틸 수 있도록 메모리 사용량을 개선한 기능이 포함됐습니다.
하지만 근본적인 문제는 해결되지 않았습니다. 리더는 여전히 동일한 계정 데이터를 두고 경쟁하는 트랜잭션을 효과적인 스팸 방지 수단 없이 선착순으로 처리했습니다. 사용자는 트랜잭션의 긴급도를 우선순위에 반영할 수 없었습니다. 이를 해결하기 위한 실용적인 장기 방안으로 세 가지 메커니즘이 제안됐습니다.
QUIC 도입: 이전에는 Solana가 UDP(User Datagram Protocol) 네트워킹 프로토콜을 사용해 RPC 노드에서 Gulf Stream을 거쳐 현재 리더로 트랜잭션을 전송했습니다. UDP는 빠르고 효율적이지만 연결이 필요 없는 방식으로, 흐름 제어와 수신 확인 기능이 없습니다. 따라서 악용 행위를 억제하거나 완화할 실질적인 방법이 없습니다. 네트워크 트래픽을 제어하기 위해 검증인의 트랜잭션 수신 프로토콜, 즉 TPU의 Fetch Stage를 QUIC으로 다시 구현했습니다.
QUIC은 TCP와 UDP의 장점을 모두 제공하려 합니다. UDP처럼 빠른 비동기 통신을 지원하면서도 TCP의 보안 세션과 고급 흐름 제어 전략을 제공합니다. 개별 트래픽 소스에 제한을 적용할 수 있어 네트워크가 정상 트랜잭션 처리에 집중할 수 있습니다. QUIC에는 분리된 스트림 개념도 있어 트랜잭션 하나가 폐기되더라도 나머지 트랜잭션은 차단되지 않습니다. QUIC은 1.13.4 릴리스에서 Solana Labs 클라이언트에 통합됐습니다.
스테이크 가중 서비스 품질(SWQoS): 검증인이 보유한 스테이크를 기준으로 네트워크 트래픽의 우선순위를 정하는 새로운 시스템이 도입됐습니다. 스테이크가 많은 검증인이 트랜잭션을 더 효율적으로 전송할 수 있도록 합니다. 이 메커니즘에서는 전체 스테이크의 3%를 보유한 검증인이 전체 패킷의 최대 3%를 리더에게 보낼 수 있습니다. SWQoS는 시빌 공격 방지 수단으로 작동해 악의적 행위자가 낮은 품질의 트랜잭션을 네트워크에 쏟아붓기 어렵게 만듭니다. 출처를 고려하지 않고 트랜잭션을 무차별적으로 수락하던 기존의 선착순 모델을 대체합니다.
우선순위 수수료 도입: 수신된 트랜잭션은 여전히 공유 계정 데이터 접근을 두고 경쟁합니다. 이전에는 단순한 선착순 방식으로 이 경합을 해결했기 때문에 사용자가 트랜잭션의 긴급도를 알릴 방법이 없었습니다. 누구나 트랜잭션을 제출할 수 있으므로 이 단계에서는 스테이크 가중 방식이 우선순위 결정에 적합하지 않습니다. 이를 해결하기 위해 Compute Budget 프로그램에 새로운 명령어가 추가됐습니다. 사용자는 실행되고 블록에 포함될 때 징수되는 추가 수수료를 지정할 수 있습니다. 수수료 대 컴퓨팅 유닛 비율에 따라 트랜잭션 실행 우선순위가 결정되어, 더 역동적이고 시장 중심적인 방식으로 트랜잭션 순서를 정할 수 있습니다.
Candy Machine 봇 세금
Metaplex는 봇 스팸에 대응하기 위해 Candy Machine 프로그램과 상호작용하는 발행 트랜잭션에 0.01 SOL의 고정 봇 세금을 신속하게 도입했습니다. 이 스팸 방지 메커니즘은 실수한 정상 사용자에게 부담을 주지 않으면서 악의적 활동을 억제하도록 최소한의 수수료를 부과했습니다. 세금은 다음과 같은 특정 상황에 적용됐습니다.
- Candy Machine이 활성화되지 않았을 때 발행 시도
- 남은 항목이 없을 때 발행 시도
- mint 또는 set collection이 마지막 명령어가 아닌 트랜잭션
- 잘못된 컬렉션 ID 사용
- 일치하지 않는 Set Collection 명령어
- 컬렉션 설정 명령어와 발행 명령어 사이의 서명자-지불자 불일치
- 허용되지 않은 프로그램이 관련된 의심스러운 트랜잭션
- 필수 허용 목록 토큰 없이 AllowList로 보호된 Candy Machine에서 발행 시도
이러한 경제적 억제책은 매우 효과적이었습니다. 발행 스나이퍼의 자금은 빠르게 고갈됐고 스팸 활동도 중단됐습니다. 처음 며칠 동안 봇 운영자들은 총 426 SOL 이상을 잃었습니다.
Durable Nonce 버그: 2022년 6월
중단 시간: 4시간 30분
근본 문제: 합의 실패로 이어진 Durable Nonce 버그
해결책:
- Durable Nonce 트랜잭션 일시 비활성화
- Solana 1.10.23 업데이트
런타임 버그로 인해 일부 Durable Nonce 트랜잭션이 두 번 처리될 수 있었습니다. recent_blockhash 필드에서 Durable Nonce 대신 최근 blockhash를 사용하면 일반 트랜잭션으로 한 번, nonce 트랜잭션으로 다시 한번 처리됐습니다. 일부 노드는 두 번째 실행을 거부하고 다른 노드는 수락해 검증인 간에 비결정적 동작이 발생했습니다. 특히 검증인의 3분의 1 이상이 해당 블록을 수락하면서 합의에 필요한 3분의 2 다수가 성립되지 못했습니다.
표준 트랜잭션과 달리 Durable Nonce 트랜잭션은 만료되지 않으므로 이중 실행을 막는 고유한 메커니즘이 필요합니다. 각 계정에 연결된 온체인 nonce 값을 사용해 순차적으로 처리하며, Durable Nonce 트랜잭션을 처리할 때마다 이 값이 교체됩니다. 교체 후에는 동일한 nonce 트랜잭션이 다시 유효해서는 안 됩니다.
문제를 완화하기 위해 Durable Nonce 트랜잭션이 일시적으로 비활성화됐습니다. 이후 Solana 1.10.23에서 수정 사항이 구현됐으며, nonce와 blockhash 도메인을 분리해 중복 실행을 방지했습니다. 업데이트 후 nonce 계정이 진행될 때 blockhash가 고정 문자열과 함께 해싱되므로 blockhash는 nonce 값으로 사용할 수 없게 됐습니다. 이에 따라 일반 트랜잭션으로 한 번 실행된 트랜잭션은 Durable 트랜잭션으로 다시 실행할 수 없으며, 그 반대도 마찬가지입니다. 또한 새로운 DurableNonce 타입이 nonce 계정 상태의 기존 blockhash 값을 대체해 타입 안전성을 높이고 향후 유사한 문제를 방지했습니다.
Durable Nonce와 활용 방법을 더 자세히 알아보려면 이전 Helius 블로그 글을 확인하세요.
중복 블록 버그: 2022년 9월
중단 시간: 8시간 30분
근본 문제: 포크 선택 규칙의 버그로 인한 합의 실패
해결책:
- 클라이언트 패치
한 검증인이 같은 블록 높이에서 실수로 중복 블록을 생성하면서 중단이 촉발됐습니다. 검증인의 기본 노드와 예비 노드가 동시에 활성화돼 동일한 노드 ID를 사용하면서 서로 다른 블록을 제안했기 때문입니다. 이러한 상태는 중단 전 최소 24시간 동안 지속됐으며, 그동안 네트워크는 해당 검증인의 중복 리더 슬롯을 올바르게 처리했습니다.
결국 네트워크는 포크 선택 로직의 버그로 인해 복구할 수 없는 포크를 만나면서 중단됐습니다. 이 버그는 블록 생성자가 이전 블록 위에 새 블록을 구축하지 못하게 해 합의 실패를 초래했습니다.
포크는 Solana에서 일상적으로 발생하며, 검증인은 일반적으로 가장 많은 투표를 받은 포크, 즉 가장 무거운 포크를 기준으로 정렬해 이를 해결합니다. 검증인이 잘못된 포크를 선택하면 네트워크와 동기화를 유지하기 위해 가장 무거운 포크로 전환해야 합니다. 하지만 이 사례에서는 가장 무거운 bank의 슬롯이 마지막으로 투표한 슬롯과 일치할 경우 검증인이 해당 bank로 되돌아갈 수 없었습니다. 이 결함 때문에 검증인이 고착됐고 합의가 더 이상 진행되지 않아 결국 네트워크가 중단됐습니다.
위 예시에서 결함이 있는 검증인 C는 리더 슬롯 5부터 8까지 중복 블록을 생성합니다. 다음 리더로 검증인 G가 들어오면 중복 블록 중 하나만 확인하고 그에 따라 포크를 확장합니다. 하지만 그다음 리더인 검증인 D는 검증인 C의 중복 블록을 모두 감지하고 폐기한 뒤 슬롯 4 위에 자체 포크를 구축합니다.
네트워크가 진행되면서 검증인 G가 구축한 포크는 과반 스테이크의 투표를 얻어 표준 체인으로 자리 잡습니다. 자신의 포크가 뒤처지고 있음을 파악한 검증인 D는 검증인 G의 포크로 전환하려 합니다. 하지만 포크 선택 로직의 버그로 인해 전환에 실패합니다. 두 포크의 공통 조상인 슬롯 5의 중복 블록이 올바르게 처리되지 않아 검증인 D가 다수 포크를 인식하지 못한 것입니다. 그 결과 검증인 D는 자체 포크에 고착돼 메인 체인에 다시 합류하지 못합니다.
코어 팀의 검토 후 문제가 해결됐습니다. 패치가 master 브랜치에 병합됐고 모든 릴리스 브랜치로 백포트됐습니다.
대형 블록이 Turbine을 압도: 2023년 2월
중단 시간: 약 19시간
근본 문제: 샤드 전달 서비스의 중복 제거 로직 실패
해결책:
- Turbine의 중복 제거 로직 및 필터링 다수 개선
- 대형 블록을 생성하면 블록 생성자가 작업을 중단하도록 강제하는 클라이언트 패치 추가
한 검증인의 맞춤형 샤드 전달 서비스가 오작동해 리더 슬롯 동안 예외적으로 큰 블록을 전송했습니다. 거의 150,000개의 샤드로 구성돼 표준 블록보다 몇 자릿수 더 컸습니다. 이 블록이 검증인의 중복 제거 필터를 압도하면서 데이터가 계속해서 재전달됐습니다. 새 블록이 생성되면서 문제가 누적돼 결국 프로토콜이 포화 상태에 이르렀습니다.
비정상적인 네트워크 트래픽이 급증해 Turbine이 압도되면서 블록 데이터는 훨씬 느린 대체 Block Repair 프로토콜을 통해 전송돼야 했습니다. Turbine은 대형 블록을 필터링해 견딜 수 있도록 설계됐지만 샤드 전달 서비스가 이 필터링 로직보다 앞단에서 작동해 효과가 약해졌습니다. 성능 저하 기간에는 블록 리더가 자동으로 투표 전용 모드로 전환됐습니다. 이는 리더가 경제적 비투표 트랜잭션을 제외하는 안전 메커니즘입니다.
근본 원인은 샤드 전달 서비스 내부의 중복 제거 로직이 실패해 샤드의 불필요한 재전송을 막지 못한 것이었습니다. 또한 재전송 파이프라인의 중복 제거 필터는 원래 Turbine 트리 내부의 루프를 방지하도록 설계되지 않아 문제가 더 심해졌습니다.
네트워크는 마지막으로 안정성이 확인된 검증인 소프트웨어 버전으로 다운그레이드해 수동으로 재시작했습니다. 이 문제를 완화하기 위해 Solana v1.13.7과 v1.14.17에서 중복 제거 로직을 개선했습니다. 필터 포화를 더 효과적으로 방지하고 네트워크 성능을 더욱 견고하게 유지할 수 있게 됐습니다.
무한 재컴파일 루프: 2024년 2월
중단 시간: 약 5시간
근본 문제: JIT 캐시에서 무한 재컴파일 루프를 일으킨 버그
해결책:
- 레거시 로더 v1.17.20 비활성화
Agave 검증인은 참조하는 트랜잭션을 실행하기 전에 모든 프로그램을 JIT(Just-In-Time) 컴파일합니다. 성능을 최적화하기 위해 자주 사용하는 프로그램의 JIT 출력을 캐시해 불필요한 재컴파일을 줄입니다. Agave v1.16에서는 기존 캐싱 메커니즘인 LoadedPrograms가 여러 효율성 개선을 도입한 ExecutorsCache라는 새로운 구현으로 대체됐습니다.
LoadedPrograms는 캐시된 프로그램에 대해 포크를 인식하는 전역 뷰를 제공했습니다. 회계 데이터 중복을 줄이고 트랜잭션 실행 스레드가 새로운 프로그램을 협력적으로 로드하게 해 컴파일 충돌을 방지했습니다. 이 시스템의 핵심 기능은 프로그램이 활성화되는 슬롯, 즉 유효 슬롯 높이를 추적해 온체인 프로그램 데이터가 업데이트될 때 캐시 무효화를 감지하는 것이었습니다.
대부분 프로그램의 유효 슬롯 높이는 온체인 계정에 저장된 배포 슬롯에서 가져왔습니다. 하지만 레거시 로더를 사용해 배포한 프로그램은 이 배포 슬롯을 계정에 보존하지 않았습니다. LoadedPrograms는 임시 해결책으로 이러한 프로그램에 유효 슬롯 높이 0을 할당했습니다.
프로그램의 바이트코드가 교체됐음을 알리는 deploy 명령어가 감지되면 예외가 발생했습니다. 이 경우 LoadedPrograms는 올바른 유효 슬롯 높이를 가진 항목을 일시적으로 삽입했습니다. 하지만 트랜잭션이 이 항목을 참조하지 않아 쉽게 축출될 수 있었습니다. 항목이 축출되면 JIT 출력은 폐기되고 프로그램은 미로드 상태로 표시됐지만 유효 슬롯 높이는 유지됐습니다.
이후 트랜잭션이 미로드 프로그램을 참조하면 LoadedPrograms는 이를 재컴파일하고 유효 슬롯 높이에 항목을 다시 삽입했습니다. 일반적으로 다음 반복에서 프로그램을 실행할 수 있게 됩니다. 하지만 레거시 로더 프로그램의 경우 새로운 JIT 출력에 센티널 슬롯 높이 0이 할당돼 이전의 미로드 항목 뒤에 배치됐습니다. 그 결과 LoadedPrograms는 프로그램이 로드됐다고 인식하지 못했고 반복할 때마다 재컴파일이 계속되는 루프가 발생했습니다.
Agave v1.16의 LoadedPrograms는 협력적 로딩을 지원하지 않았기 때문에 문제를 촉발한 트랜잭션이 블록에 포함될 수 있었습니다. 이 블록이 네트워크 전체에 전파되자 모든 검증인이 이를 재실행하면서 같은 무한 재컴파일 루프에 빠졌습니다. 중단 당시 클러스터 스테이크의 95% 이상이 Agave v1.17을 실행하고 있어 대부분의 검증인이 이 블록에서 정체됐고 네트워크가 중단됐습니다.
이 버그는 전주에 Devnet 클러스터 중단을 조사하는 과정에서 확인됐고 패치 배포가 예정돼 있었습니다. 선택된 완화책은 변경 사항을 Agave v1.17로 백포트하고 네트워크 재시작 시 feature gate를 즉시 제거하는 것이었습니다. 이를 통해 버그를 촉발한 레거시 로더를 비활성화해 재발을 방지했습니다.
조율된 취약점 패치: 2024년 8월
중단 시간: 없음
근본 문제: 잘못된 ELF 주소 정렬 가정
해결책:
- 패치 업데이트
8월 5일, 외부 연구원이 보고한 Agave 클라이언트 취약점이 Anza 코어 엔지니어에게 전달됐습니다. 공격자가 이 결함을 악용하면 리더 검증인을 중단시켜 네트워크 전체를 멈출 수 있었습니다. Anza 엔지니어는 신속하게 패치를 개발했고 여러 외부 보안 업체가 이를 감사했습니다.
Solana 프로그램은 LLVM을 사용해 ELF(Executable and Linkable Format)로 컴파일됩니다. 취약점은 생성된 ELF 파일 내부의 잘못된 주소 정렬 가정에서 비롯됐습니다. ELF 무결성 검사는 일반적으로 여러 무결성 검사를 적용하지만 .text 섹션의 정렬은 검증하지 않았습니다. 이 누락으로 악의적으로 조작된 ELF 파일이 잘못 정렬된 .text 섹션을 정의할 수 있었고, 가상 머신이 유효하지 않은 주소로 이동할 가능성이 생겼습니다. 이 경우 호스트 세그멘테이션 오류가 발생해 검증인이 중단됩니다.
공격자는 다음과 같은 방식으로 이 취약점을 악용할 수 있었습니다.
- CALL_REG opcode를 사용하는 악성 Solana 프로그램 생성
- ELF 파일을 조작해 .text 섹션을 잘못 정렬
- 네트워크에 프로그램을 배포하고 호출해 검증인 중단 유발
패치 업데이트 절차
패치 업데이트를 공개하면 취약점의 내용도 즉시 모두에게 드러납니다. 충분한 스테이크가 업그레이드하기 전에 공격자가 취약점을 역설계해 네트워크를 중단할 시간을 얻을 수 있습니다. 이런 상황을 피하려면 임계 규모의 검증인이 패치 릴리스를 최대한 신속하게 적용해야 합니다.
8월 7일까지 Solana Foundation의 여러 구성원이 다양한 커뮤니케이션 플랫폼에서 검증인에게 비공개 메시지를 보냈습니다. 예정된 중요 패치를 알리고 사고 날짜와 고유 식별자를 확인할 수 있는 해시 메시지도 공유했습니다. Anza, Jito, Solana Foundation의 여러 주요 인사는 메시지의 정확성을 검증할 수 있도록 X, GitHub, LinkedIn에 이 해시를 공유했습니다. 공유된 해시의 예시는 다음과 같습니다.
다음 날까지 코어 구성원들은 검증인에게 계속 연락하며 긴급성과 기밀 유지의 중요성을 강조했습니다. 사전에 정해진 8월 8일 오후 2시(UTC)에 검증인 운영자들은 패치 다운로드, 검증, 적용 방법이 담긴 추가 메시지를 받았습니다. 패치는 Agave 메인 저장소가 아니라 잘 알려진 Anza 엔지니어의 Github 저장소에서 제공됐습니다. 지침에는 다운로드한 패치 파일을 제공된 shasum과 대조해 검증하는 절차가 포함됐습니다.
8월 8일 오후 8시(UTC)까지 압도적 다수의 스테이크에 패치가 적용돼 네트워크 보안이 확보됐습니다. 이후 취약점과 해당 패치가 공개됐으며, 남은 모든 검증인에게 업그레이드를 요청했습니다.
패치의 비공개 배포와 검증인 간의 물밑 조율은 Solana의 탈중앙화에 대한 우려를 불러일으켰습니다. 사고 직후 Solana Foundation의 전무 이사 Dan Albert가 미디어 인터뷰에서 이러한 비판에 답했습니다.
“중앙화와 조율 능력을 혼동하지 않는 것이 중요하다고 생각합니다. 전 세계에는 거의 같은 수의 개인이 운영하는 블록 생성 노드 1,500개가 있습니다…. 이들 전체 또는 일부와 자발적으로 소통할 수 있다는 사실을 중앙화와 혼동해서는 안 됩니다.”
중앙화와 조율 능력을 혼동하지 않는 것이 중요하다고 생각합니다. 전 세계에는 거의 같은 수의 개인이 운영하는 블록 생성 노드 1,500개가 있습니다…. 이들 전체 또는 일부와 자발적으로 소통할 수 있다는 사실을 중앙화와 혼동해서는 안 됩니다.

결론
이 글을 작성하는 시점 기준으로 Solana는 1년 넘게 중단 없이 운영됐으며, 메인넷 베타에서 “베타” 표시를 제거하기 위한 핵심 이정표를 달성했습니다. 네트워크가 성숙하면서 중단 빈도는 줄어드는 것으로 보입니다. Firedancer가 도입되면 클라이언트 다양성이 향상돼 발견되지 않은 버그나 엣지 케이스로 전체 클러스터가 중단될 위험도 줄어들 것으로 예상됩니다. 하지만 Helius 창립자 Mert Mumtaz를 비롯한 일부 커뮤니티 리더는 중단이 계속될 것이라고 전망했습니다. 결과는 시간이 말해줄 것입니다.
이 글의 이전 버전을 검토해 주신 Zantetsu(Shinobi Systems)와 OxIchigo에게 깊이 감사드립니다.
추가 자료
관련 아티클
Helius 구독하기
최신 Solana 개발 소식을 확인하고 새 게시물 알림을 받아보세요


