Gaia-9001은 왜 멈췄는가?

EJ Lee
Cosmonauts In Korea
7 min readDec 18, 2018
이미지 출처 : https://medium.com/@irisnet/how-to-join-cosmos-testnet-gaia-9001-d050cee5de81

안녕하세요 CIK 에디터 이의준입니다!

다들 올해 안에 메인넷 런칭이 가능할 것이라는데에 대한 기대감이 높던 11월, Gos를 직전에 두고, 잘 돌아가던 Gaia-9001 테스트넷이 정지되는 문제가 있었습니다. 이에 따라 올해 안에 메인넷을 런칭하는 것은 좀 많이 힘들게 되었고, 이 점에 대해서 BUIDL SEOUL 2018에서 재권역시 내년에 메인넷이 런칭될 것 이라는 뉘양스의 이야기를 했습니다. 오늘은 Gaia-9001 테스트넷을 정지시킨 두가지 이슈에 대해서 알아보도록 하겠습니다. 잘못된 내용에 대한 지적과 글에 대한 피드백은 언제나 환영합니다.

이번 Gaia-9001의 버그는 크게 두가지 버그로 볼 수 있습니다.

1. AnteHandler Bug

첫번째 버그는 Antehandler 버그입니다. 이 버그에 대한 이슈는 https://github.com/cosmos/cosmos-sdk/issues/2772 에서 확인할 수 있습니다. QOSChain의 중국인 개발자인 kauchy가 처음 제기한 버그입니다.

AnteHandler라는 것은 트랜잭션의 기본적인 유효성을 검증해주는 auth 모듈 안의 기능입니다. 모든 트랜잭션들은 StdTx로 정의되고, 브로드캐스팅 된 후, AnteHandler에서 트랜잭션의 서명이 올바른지, 수수료가 적합한지, 시퀀스가 올바른지를 확인합니다. AnteHandler에서 트랜잭션의 유효성이 검증되면, 이후 트랜잭션은 라우터 -> 키퍼들로 가서 상태를 변화시킵니다. 자세한 내용은 https://medium.com/caulink/understanding-cosmos-universe-part-1-9295d8102c2e 를 참고하시면 도움이 될 것 같습니다.

본격적으로 AnteHandler 버그에 대해서 설명하기 전에, 알아야 할 요소들에 대해서 간단하게 설명하도록 하겠습니다.

1)FeeCollectionKeeper

AnteHandler 안에는 FeeCollectionKeeper라는 기능이 있는데, FeeCollectionKeeper는 AnteHandler로 들어온 트랜잭션들의 수수료를 모으는 역할을 합니다.

2)signerAccs[0]

첫번째로 서명한 계정입니다.(signer accounts에서 첫번째/ 프로그래밍에서 숫자는 0부터 시작) StdTx에서는 signerAccs[0]의 계좌에서 수수료가 차감됩니다.

3)CheckTx

어플리케이션(SDK/안티핸들러)에게 트랜잭션이 유효한가를 묻는 메세지, ABCI 메세지입니다. DeliverTx와 달리, CheckTx는 상태를 변화시키지 않습니다.

4)DeliverTx

트랜잭션이 블록에 담겨서 그 블록이 정상적으로 커밋되었을 때, 이 트랜잭션을 받은 노드는 DeliverTx를 통해 자신의 상태를 변화시킵니다. 이 역시 ABCI 메세지입니다.(텐더민트는 선 합의 후 실행)

5)Feepool

블록에 포함된 트랜잭션들의 수수료들을 모아놓은 값입니다. 이는 검증인들과 위임자들에게 분배됩니다.

6)블록 프로포저(Block Proposer)

텐더민트 합의 알고리즘에서 새로운 라운드가 시작될 때, mempool에 있는 트랜잭션들을 모아 블록을 다른 노드들에게 propose 하는 검증인 노드입니다.

AnteHandler 버그의 핵심은 블록 프로포저가 CheckTx를 생략할 수 있다는 것, 그리고 다른 검증인들은 DeliverTx만 실행한다는 것입니다. 공격자 노드는 잘못된 퍼블릭 키를 사용해 트랜잭션을 만들어서 브로드 캐스팅 할 수 있습니다. 이 트랜잭션은 벨리데이터 노드에게 전달됩니다. 이 받은 트랜잭션을 벨리데이터 노드에서는 mempool에 담기 이전에 CheckTx를 통해 트랜잭션을 AnteHandler로 보내 기본적인 적합성 검증을합니다. 정상적이라면, 이 과정에서 올바르지 않은 트랜잭션들이 mempool에 담기지 않지만, 이번 버그때는 벨리데이터 노드가 CheckTx의 결과를 무시하고 mempool에 트랜잭션을 담을 수 있었습니다.

mempool에 담긴 트랜잭션이 공격의도를 가진 블록 프로포저에 의해 블록에 담기면, 해당 라운드에서 문제 없이 커밋 될 가능성이 높습니다, 텐더민트의 로직은 ‘블록’의 유효성을 검증하는 것에서 그치지, 블록 내부의 ‘트랜잭션’을 검증하지는 않기 때문입니다. 이렇게 정상적으로 커밋 된 블록은 블록체인에 추가되고, 다른 검증인들은 DeliverTx를 통해서 이 블록을 사용해 상태를 변화시키는데, 이때 AnteHandler에서는 signerAccs[0]의 유효성을 검증하기 이전에, FeeCollectionKeeper에서 먼저 트랜잭션의 수수료를 차감합니다. 이는 코스모스 허브가 Account Model을 사용하고 있기 때문에, 트랜잭션의 시퀀스를 통해 트랜잭션 재사용을 방지해야 하기 때문입니다. 따라서 AnteHandler로 들어온 트랜잭션의 수수료를 먼저 차감한 후에, 서명을 검증합니다. 이 말은, 서명이 잘못되어 있더라도 수수료가 먼저 차감 된다는 것입니다. 이때, 이 수수료는 공격자(잘못된 서명을 쓴 Tx을 전파한 공격자)의 계정에서 차감되지 않습니다. 왜냐하면 그 트랜잭션의 서명은 잘못된 서명이며, 공격자의 서명과 무관하기 때문입니다. 하지만 이 수수료는 feepool에는 추가됩니다. 서명검증 이전에 수수료를 먼저 차감하기 때문입니다. 이는 결과적으로 수수료로 지불된 토큰의 발행량을 늘리는 결과를 낳습니다.

코스모스팀에서는 안티핸들러에도 핸들러에서 처리하는 것처럼 유효성 검증 실패시에는 상태 기록을 안하도록 한번 감싸주는 것으로 해결하려 합니다.

2. Negative fee Bug

두번째 버그는 negative fee 버그인데 이 버그가 직접적으로 Gaia-9001을 멈추게 한 버그입니다. 말 그대로, 트랜잭션의 수수료를 음수로 설정하고 보낼 수 있다는 것입니다. 공격자가 본인의 서명을 사용해서 StdTx.fee를 음수로 설정하고 트랜잭션을 보내고, 공격자에 의해 이 트랜잭션이 블록에 담겨 생성된다면, 이 트랜잭션은 feepool의 수수료들을 차감합니다. 이런 방식으로 계속 StdTx.fee가 음수인 트랜잭션을 만들고, 그 트랜잭션들을 담는 블록의 feepool이 음수가 되고, 이후 다른 모듈에서 feepool 관련 연산을 할 때에 에러가 나서 체인이 멈추게 됩니다.

이 버그는 단순히 coins.Amout를 uint로 바꾸거나, fee가 positive인지 Safety Measure를 추가하면 일어나지 않았을 버그입니다. 실제로 coins.Amout를 uint로 바꾸자는 이슈가 6월 16일에 https://github.com/cosmos/cosmos-sdk/issues/1273 있었지만, 다른 개발이 바빠서 논의가 미뤄지기도 했고, 코스모스 내부적으로 토큰 차감 등에 음수를 사용하기 때문에 int를 지속적으로 사용하기로 했습니다. 이번 버그 이후에는 int를 uint로 수정하려다가, 이런 접근방식은 힘든 부분이 많아서, Safety Measure를 추가하는 방식으로 이번 버그를 해결하려 합니다. 자세한 내용은 https://github.com/cosmos/cosmos-sdk/pull/2797 에서 확인 할 수 있습니다.

Special thanks to Thunnini

--

--

EJ Lee
Cosmonauts In Korea

Chung-Ang Univ, C-Link Research team, Interested in Cryptofinance