공격 사례로 톺아보는 테라의 Price Oracle 디자인
제 인턴 기간도중 테라 블록체인에서 발생한 다양한 공격 사례들을 통해서 테라 블록체인의 Price Oracle 디자인이 어떻게 변화하였는지 살펴보도록 하겠습니다.
테라란 무엇인가?
테라는 알고리즘 기반 스테이블 코인 프로젝트로, PoS 기반의 블록체인을 사용합니다. 루나라는 암호화폐를 담보로 각 나라의 화폐에 페깅(가치가 고정된) 스테이블 코인을 발행할 수 있습니다. 또한 실시간으로 한 국가의 명목화폐에 페깅된 스테이블 코인을 다른 나라의 화폐에 페깅된 스테이블 코인으로 스왑할 수 있는 기능도 지원합니다.
추가적인 디테일은 여기 를 참고해주세요.
Price Oracle이란 무엇인가?
Oracle이라는 단어의 뜻은 예언자라는 뜻으로 신과 인간 사이에서 신탁을 전달하는 사람, 혹은 신탁 그 자체를 의미합니다.
블록체인 그 자체로는 외부 데이터를 인지하는 능력이 없습니다. 따라서 외부 데이터를 블록체인 내부에서 사용하기 필요한 것이 오라클입니다. 외부 데이터가 신의 신탁, 블록체인이 신탁을 원하는 인간, 오라클이 신의 신탁을 인간에게 전달해주는 예언자라고 생각하면 이해가 편합니다.
오라클이 블록체인에 리포팅하는 데이터의 종류를 바탕으로 오라클을 나눌 수 있는데, 이번 글에서 다루는 오라클은 시장 가격 데이터를 블록체인으로 리포팅 해주는 Price Oracle입니다.
왜 테라에는 Price Oracle이 필요한가?
현재 테라 블록체인에서 Price Oracle이 필요한 이유는 두가지입니다.
테라 <> 루나 스왑 지원을 통해서 테라 스테이블코인 페깅 유지
테라 <> 테라 스왑 지원
앞서 잠시 언급했듯, 테라 블록체인은 네이티브 코인인 루나와, 스테이블 코인 테라 사이의 페깅 유지를 위해서 루나와 테라 사이의 스왑을 지원합니다. 이에 대해 간략하게 설명하자면, 만일 테라 스테이블코인의 공급 과잉으로 테라 스테이블코인 가격이 기준가 이하로 하락한다면, 차익거래자는 테라를 시장에서 구매 이후, 테라 블록체인의 온체인 마켓에서 해당 테라를 루나로 스왑합니다. 이후 루나를 사장에 판매하여 차익거래를 실현합니다. 반대로, 공급 부족으로 인하여 테라 스테이블 코인의 가격이 기준가 이상으로 상승한다면, 차익거래자는 루나를 시장에서 구매한 후, 이를 테라 블록체인의 온체인 마켓에서 루나를 테라로 스왑한다. 이후 테라를 시장에 판매하여 차익거래를 실현합니다.
해당 차익거래 과정에서, 테라 블록체인의 온체인 마켓에서의 테라와 루나간의 교환 비율을 정하기 위해서 루나의 시장 가격 데이터가 요구됩니다.
또한, 테라 블록체인에서는 테라 스테이블 코인간 스왑을 지원합니다. 해당 기능을 사용하여 1 Terra USD를 같은 가치의 Terra KRW로 교환할 수 있습니다. 이때 역시, 교환 비율을 책정하기 위해서 환율 데이터가 요구됩니다.
테라 블록체인의 Price Oracle 구조

테라 블록체인의 Price Oracle을 살펴보기 위해서 우리가 알아야 하는 로직은 크게 두 부분이라고 할 수 있습니다. 첫 번째는 Off-chain logic입니다. Off-chain logic이란, 검증인들이 데이터 제공자들에게서 받은 데이터를 가공하여 블록체인에 제출할 Prevote와 Vote를 생성하기 까지의 로직을 의미합니다. 두 번째는 On-chain logic입니다. 이는 테라 블록체인 내부의 오라클 모듈의 로직을 의미합니다.
Off-chain logic : 검증인들의 Price Oracle
테라 블록체인의 검증인들은 다양한 오라클을 개발하여 사용하고 있습니다. 각각의 검증인들이 사용하고 있는 오라클의 구조가 다르기 때문에, 이번 글에서는 Terraform labs에서 관리하고 있는 검증인들의 오라클 구조를 기반으로 오라클을 검증합니다.

TFL에서 관리하고 있는 검증인들이 사용하는 Price Oracle은 위와 같은 구조를 가집니다. 먼저 Fetcher는 각 Data provider들로부터 Price data(OHLCV와 오더북 데이터)를 받아와 Price store에 저장하는 역할을 합니다. 이후 Pre-processor는 Price store에 저장된 데이터들을 사용하여 보팅에 사용할 가격 데이터를 생성합니다. 이때 Pre-processor는 Price store에 저장된 Price data들을 바탕으로 다양한 기준 가격 결정 알고리즘을 적용해서 블록체인에 제출할 기준 가격을 만들 수 있습니다. 예를 들어 오더북 데이터를 사용해 Mid price를 기준가격으로 제시하거나, 1분봉 종가를 사용한 15분 이동평균 가격을 사용하여 기준가격을 제시하는 것이 그 예시가 될 수 있습니다. 마지막으로, Voter는 생성된 기준가격을 바탕으로 Transaction(Prevote, vote)를 생성하여 제출하는 역할을 합니다. 해당 트랜잭션, Prevote와 Vote가 무엇이며, 어떻게 블록체인에 반영되는지에 대해서는 바로 다음 섹션에서 서술하겠습니다.
On-chain logic : 테라 블록체인의 Oracle 모듈
테라 블록체인은 체인 외부의 가격 정보를 블록체인 위로 불러오기 위하여 오라클 모듈을 사용합니다. 오라클 모듈은 현재 루나의 시장 가격과, 각각의 테라 통화쌍 간의 교환 비율을 투표로 결정합니다. 투표는 검증인들에 의해 이루어지며, 이때 총 보팅파워의 반 이상이 투표되어야지만 해당 투표가 집계될 수 있습니다. 보팅파워의 절반 이상이 투표가 완료되는 경우, 해당 투표의 중위값이 교환 비율로 결정됩니다.
테라의 오라클 투표는, 전형적인 commit-reveal 형식의 투표로, 한 시점(Vote_period)의 가격을 총 두번의 투표로 결정합니다. 이때 하나의 Vote_period는 한개 이상의 블록으로 구성됩니다.

N 시점에서의 가격을 정하기 위한 첫번째 투표는 N-2 시점에서 행해지는 Prevote입니다. Prevote 투표는 테라 통화들과 루나의 교환비율을 SHA256 함수로 해싱한 값을 담고 있습니다. 예를 들어, KRW, USD, SDR에 페깅된 테라 통화들과 루나에 대한 온체인 스왑을 지원하기 위해서는, 각각 Luna <> KRW, Luna <> USD, Luna <> SDR에 해당하는 세가지 교환 비율과 salt 값을 해싱한 값이 포함된 prevote가 제출되어야 합니다. 여기서 salt값이란 일종의 비밀번호라고 볼 수 있습니다. 만일 교환 비율만 해싱하여 제출하게 된다면, 현실적으로 가능한 교환 비율이 정해져 있기 때문에, 무작위 대입을 통해 교환 비율을 유추할 수 있게 됩니다. 이를 방지하기 위해, salt라는 일종의 추가적인 비밀번호를 교환 비율과 같이 해싱하여 교환 비율을 유추할 수 없게 합니다.
두번째 투표는 N-1에 제출되는 Vote입니다다. N-1 시점에 제출된 Vote는 N-2에서 제출된 prevote에서 해시값을 만드는데 사용된 salt값과 실제 교환비율을 공개합니다. N-1 시점에 제출된 Vote의 교환 비율들을 기반으로 N 시점의 오라클 가격을 결정하게 됩니다. 테라간 온체인 스왑의 경우에는 위에서 결정된 교환 비율들을 통해 온체인 환율을 계산하여 이루어 집니다.
Prevote에서 해시값을 제출하는 이유는, 만일 오라클 투표를 해시값 제출 없이 공개 투표로 진행하게 된다면, 검증인들은 해당 투표 기간의 마지막까지 기다렸다가, 제출된 투표들의 중위값을 계산해서 투표를 제출하는 것을 통해 안정적으로 보상을 얻을수 있게 되기 때문입니다. 이를 방지하기 위해 테라의 오라클 모듈은 commit-reveal 형식의 투표를 사용하게 된 것입니다. 하지만, 이러한 commit-reveal 형식의 투표는 N-2 에서 관측한 값을 N-1에서 공개하고, N에서 사용하기 때문에, N 시점의 오라클 가격이 N-2 시점의 시장 가격을 나타나게 되는 단점이 있습니다. 즉 실제 시장 가격과 오라클 가격 사이의 딜레이가 발생하게 됩니다. col-2 당시 Vote_period는 12블록으로, 블록타임을 약 6초라고 이야기 할 때, col-2 당시, 테라 블록체인의 오라클 가격과 실제 시장 가격 사이에는 최소 72초(12블록) ~ 최대 144초(24블록) 사이의 딜레이가 발생할 수 있습니다

이 commit-reveal 구조로 인해 필연적으로 발생하는 실제 시장 가격과 테라 블록체인의 오라클 가격간의 괴리는 향후 서술할 Front-running 공격의 원인이 됩니다.
오라클에 대해서는 충분히 설명한 것 같으니, 첫 번째 공격부터 살펴보겠습니다.
시장조작을 통한 루나<>테라 차익거래 - 1
제가 인턴 기간동안 마주한 첫 번째 공격사례는 시장 조작을 통한 루나와 테라간 차익거래였습니다. 해당 공격은 총 21개의 온체인 스왑을 통해서 차익 실현되었고, 공격자는 당시 시가 기준으로 약 1400만원의 수익을 가져갔습니다.

당시 공격자의 공격 방식을 차트를 통해서 분석해 보겠습니다.

스프레드 조작 (9:22 ~ 11:06)
9시 22분, 9시 35분, 9시 39분에 이루어진 시장가 매도로 인해서 1750~1700 사이의 스프레드가 발생합니다.
이후 누군가가 1750~1700 사이를 매도 주문들로 채웠고, 또 누군가가 해당 매도 주문들을 시장가 매수를 통해서 없애버립니다.
이후 호가(1750~1700)는 빈 상태로 유지되고, 이후 누군가가 11시 3분부터 6분까지 1700원~1615원의 호가를 지속적인 시장가 매도를 통해서 비우게 됩니다.
이를 통해서 1750~1615원 사이의 빈 호가가 완성됩니다.
오라클 조작 (10:57 ~ 13:40)
공격자는 10시 57~59분 사이에 빈 호가에서 가장 위 호가에 소량의 루나를 계속 구매하는 것을 통해 해당 가격이 오라클 가격에 반영될 때 까지 기다린 것으로 보입니다. 이는 망치형 캔들과 굉장히 적은 거래량(약 10 루나)을 통해서 추측할 수 있는 부분입니다.
공격자는 11시에 오라클 가격이 반영 되자마자 온체인에서 7만개의 루나를 KRT로 오라클 가격 1748(실제 온체인 스프레드 제거하면 약 1701)에 스왑합니다.
이후 공격자는 1615원까지의 아래 호가를 시장가 매도를 통해서 없앱니다.
이후 11시 6~9분 사이에 가장 아래 호가에 소량의 루나를 계속 구매하는 것을 통해 오라클 가격에 공격자가 조작한 가격이 반영되도록 합니다.
시장 조작으로 오염된 오라클 가격이 11시 11분에 반영되는 순간, 공격자는 스왑을 통해서 KRT를 다시 루나로 바꿉니다. 이러한 과정을 반복해서 공격자는 차익거래를 완성합니다.
이를 통해서 공격자의 공격 과정은 다음과 같이 유추할 수 있습니다.
시장가 매수 매도를 통해서 호가창에 큰 스프레드 만들기
소량의 루나를 조작된 Best ask가격에 지속적으로 매수
오라클이 높은 가격으로 조작되면 루나를 테라로 스왑
이후 다시 루나를 조작된 Best bid 가격에 지속적으로 매도
오라클이 낮은 가격으로 조작되면 테라를 루나로 스왑
2~5 반복
이러한 공격자의 시장 조작이 가능했던 이유는 크게 두 가지로 볼 수 있습니다. 첫 번째는 코인원 루나 마켓의 낮은 유동성입니다. 당시, 코인원 루나 마켓의 유동성은 매우 형편없는 수준으로, 아주 적은 비용만 투자해도 시장 조작이 가능한 상황이었습니다. 두 번째는 당시 검증인들의 Price oracle의 기준 가격 결정 알고리즘 때문입니다. 당시, 대부분의 검증인들이 1분봉 종가를 기준으로 투표를 진행하고 있었습니다. 이는 1분 가장 마지막에 채결된 가격을 사용하는 것과 동일합니다. 결국 이 공격은 매우 조작에 취약한 검증인들의 기준 가격 결정 알고리즘과 조작에 취약한 낮은 유동성을 가진 시장이 맞물려 발생한 것이라고 이야기 할 수 있습니다.
해당 공격 이후, 저희는 시장 조작 비용을 증가시키기 위해 검증인들의 기준 가격 결정 알고리즘에 15분 이동평균 도입을 권장했습니다.
시장조작을 통한 루나<>테라 차익거래 - 2
하지만 15분 이동평균을 도입하였음에도 불구하고, 일주일 후, 같은 방식의 차익거래 공격이 다시 발생했습니다. 두 번째 공격은, 총 9개의 스왑 트랜잭션으로 구성되어 있고, 공격자는 당시 시가 기준으로 약 1500만원의 수익을 가져갔습니다. 같은 차익거래 공격이 다시 발생한 이유는 정말 간단합니다. 코인원 루나 마켓의 유동성이 너무 낮아, 검증인들의 기준가격 결정 알고리즘에 15분 이동평균을 도입한 것으로도 충분하지 않았기 때문입니다. 실제로 15분 이동평균을 도입한것 그 자체는 효과가 있었습니다.


위 두 그래프는 각각 첫 번째 차익거래와 두 번째 차익거래 당시의 코인원 루나 마켓의 가격과, 테라 블록체인의 오라클 가격을 나타낸 그래프입니다. 파란색 선은 코인원 루나 마켓의 가격을 나타내며, 노란색 선은 테라 블록체인의 오라클 가격을 나타냅니다. 위 두 그래프를 비교하는 것을 통해서 저희는 공격자가 오라클 가격을 조작하는데 첫번째 공격에 비해서 상대적으로 더 오랜 시간을 들였다는 것을 알 수 있습니다.
저희는 당시 추가적인 개선안으로 검증인들의 기준가격 결정 알고리즘에 30분 Mid candle price 이동평균을 권장했습니다. 보통 Mid price란, 호가창의 Best bid 가격과 Best ask 가격의 평균을 의미하지만, 당시 대부분의 검증인들이 1분봉 OHLCV 데이터만 사용하고, 호가창 스냅샷을 사용하고 있지 않았기 때문에, Best bid와 Best ask를 각각 1분봉 고가와 저가로 대체한 Mid candle price를 사용하기로 했습니다. 또한 해당 Mid candle price 30개를 평균낸 가격, 즉 30분 이동평균을 추가적으로 도입해서 기존 15분 이동평균에 비해 오라클 가격 조작에 더 많은 시간과 비용이 소요되도록 하였습니다.
하지만, 기준가격 결정 알고리즘을 시장 조작 비용을 늘리는 방향으로 변경하는 것은 한가지 단점을 가지고 있습니다. 바로 실제 시장 가격과 오라클 가격간의 편차를 증가시킨다는 것입니다. 이동 평균 가격은 시장 가격보다 느리게 움직입니다. 만일 시장 가격이 급격하게 변동한다면, 이동 평균 가격과 시장 가격의 차이를 통해서 또 다른 차익거래 기회가 열릴 가능성이 존재합니다.
그럼에도 불구하고 당시 저희가 30분 Mid candle price 이동평균을 개선안으로 채택한 이유는, 당시 코인원 루나 마켓의 유동성이 매우 낮았기 때문입니다. 낮은 유동성을 가진 시장은 시장 조작에 취약한 반면, 시장 가격과 오라클 가격간 편차를 사용한 차익거래는 이루어지기 어려운 특성을 가지기 때문입니다.
시장가격과 오라클 가격 간의 딜레이를 사용한 테라<>테라 프론트 러닝
두번의 공격 이후, 한달 정도 지나, 또 다른 차익거래 공격이 발생했습니다. 이번에 발생한 차익거래는 지난 두번의 공격과 달리, 테라 통화들간 스왑을 사용한 공격이었습니다. 이번 공격에서 공격자는 약 155만원의 수익을 발생시켰습니다.
시장 조작에 많은 비용이 들지 않았던 당시 코인원 루나 마켓과 달리, 테라<>테라 스왑의 기준가격의 바탕이 되는 시장은 외환 시장입니다. 외환 시장은 매우 유동성이 높은 시장으로, 시장 조작이 불가능하다고 이야기해도 무방할정도로, 시장 조작 가격이 매우 높은 시장입니다. 테라 블록체인에서 차익거래를 진행하기 위해서 외환 시장을 조작한다는 것은 어불성설입니다.
이번 테라<>테라간 차익거래 공격은, 시장 조작이 아닌, 앞서 언급한 실제 시장 가격과 오라클 가격간 딜레이를 레버리지한 프론트 러닝의 형식으로 이루어졌습니다. 저희는 처음에는 공격자의 공격 과정이 다음과 같을거라고 예상했습니다.
환율이 상승하는 상황에서, Vote_period가 끝나기 직전까지 oracle 환율과 현실 세계의 환율간의 편차(deviation)이 존재한다면 해당 Vote_period 안에서 스왑 진행
다음 오라클 가격 반영 시, 해당 편차가 반영되는 즉시 반대 방향으로 스왑 진행을 통해서 해당 Front-running 포지션을 청산
1 ~ 2 반복
하지만 실제로 공격자는 저렇게 복잡하게 공격하지 않았고, 오라클 투표 과정의 Vote단계의 트랜잭션들을 사용해 다음 오라클 가격을 추정하여 스왑하는 것으로 밝혀졌습니다. 어찌 되었건 프론트 러닝을 사용한 공격이라는 것은 변함 없습니다.
이러한 프론트 러닝이 가능했던 이유는 크게 두가지입니다. 첫 번째로, commit-reveal 투표 구조로 인한, 실제 시장 가격과 블록체인 오라클 가격 간의 딜레이 때문입니다. 두 번째는, 당시 테라 통화간 스왑에는 가스비를 제외한 다른 수수료가 없었다는 점입니다. 가스비를 제외한 다른 수수료가 존재하지 않았기 때문에, 외환 시장의 낮은 변동성 하에서도 차익거래가 가능했던 것입니다.
이러한 공격을 방지하기 위해서, 저희는 테라 통화간 스왑에도 프론트 러닝 방지를 위한 수수료를 도입하기로 결정했습니다.
해당 수수료를 결정하기 위해서, 저희는 당시 테라 통화쌍들 중, 가장 변동성이 큰 USD/KRW 쌍에 대해서 2014년 부터 2019년까지의 1분봉 데이터를 수집해 Historical VaR를 계산했습니다. 해당 VaR 계산을 위해서, 당시 저희는 연속되는 2개의 1분봉에서 관측되는 최대 편차를 수집했습니다. 이렇게 연속되는 2개의 1분봉의 최대 편차, 즉 2분 최대 편차를 구한 이유는 2분이라는 시간이 실제 시장 가격과 테라 블록체인의 오라클 가격 간 딜레이와 가장 비슷하기 때문입니다. 해당 VaR 계산의 결과는 다음과 같습니다.


계산된 Historical VaR은 최대 편차, 즉 최악의 상황만을 고려했기 때문에, 실제 시장 가격과 오라클 가격간의 편차 리스크는 계산된 Historical VaR보다 낮을 가능성이 매우 높습니다. 하지만 저희는 최악의 상황을 가정하는 것을 통해 Front-running의 가능성을 최대한 배제하는 것이 중요하다고 생각해서 해당 결과값을 수수료 책정에 사용했습니다.
또한 추가적으로 기존 12블록이었던 Vote_period를 5블록으로 변경하여 딜레이 자체를 반으로 줄였습니다.
결론
테라는 다양한 공격들을 기반으로 오라클 디자인을 변화시켰습니다. 견고한 Defi를 디자인 하는 것은 매우 어려운 일인것 같습니다.
