Skip to content
isdnetworks
Go back

카나리 전에 다른 토큰을 센다

(수정됨)

멈춘 ETH 계열 출금을 대량으로 재전송하기 전에 한 건으로 카나리를 돌리기로 했다. 대상 하나를 고르고 그 출금의 status 를 확인한 뒤 조작 직전에 하나를 더 봤다.

Table of contents

Open Table of contents

제약 — 주소에 토큰별 출금이 붙는 구조

이 시스템은 핫월렛 주소 하나에서 토큰별 출금이 나간다.

핫월렛 주소 X
  ├─ ETH 출금      nonce 41 (멈춤)
  ├─ 토큰 A 출금   nonce 42 (대기)
  └─ 토큰 B 출금   nonce 43 (대기)

그런데 화면의 재전송 버튼은 pending_withdrawals 행이 아니라 주소의 nonce 단위로 동작한다.

의도한 출금만 나가는 것이 아닐 수 있다는 뜻이다. 같은 주소에서는 nonce 가 순서대로 처리되므로 41 을 수수료를 올려 대체하면 뒤에 막혀 있던 42 와 43 이 함께 풀린다. 한 출금만 보고 안전하다고 판단하면 다른 토큰에서 일어나는 변화를 못 본다.

검증 — 같은 주소의 대기 건을 셌다

스냅샷 쿼리에 서브쿼리를 하나 넣었다.

SELECT
  pw.id, pw.coin, pw.status, pw.nonce, ...,
  (SELECT COUNT(*) FROM pending_withdrawals p2
    WHERE p2.from_address = pw.from_address
      AND p2.broadcast_at IS NOT NULL
      AND p2.confirmed_at IS NULL) AS pending_same_address
FROM pending_withdrawals pw
WHERE pw.id = ?;

pending_same_address 로 그 주소의 미확정 대기 건 수를 같이 뽑는다.

결과는 이랬다.

pending_same_address  1

confirmed_at 이 비어 있는 행이 이것 하나뿐이라 다른 토큰으로 파급될 행 자체가 없었다.

판단 기준 — 1이면 안전하다

pending_same_address 가 1이면 파급이 구조적으로 불가능하다.

2 이상이면 둘 중 하나를 고른다.

[선택 1]  그 출금을 카나리 대상에서 뺀다
[선택 2]  다른 토큰 출금까지 함께 나가는 것을 사전 합의하고 진행

선택 2 를 버리고 선택 1 을 골랐다. 이유는 어느 쪽을 택하든 모르는 채로 진행하는 것보다는 낫지만 대상을 바꾸는 편이 간단해서다.

주의 — 검증 결과의 오염

pending_same_address 가 2 이상인 출금은 결과 해석을 어렵게 만든다.

카나리 결과가 이상함
    ↓
내 재전송 때문인가, 함께 풀린 다른 토큰 출금 때문인가
    ↓
구분이 안 된다

카나리의 목적은 그 재전송 하나의 효과를 보는 것인데 다른 변수가 섞이면 그것을 못 본다.

카나리 대상은 변수가 적어야 한다. 대기 건이 여럿인 주소는 변수가 여럿이고 하나뿐인 주소는 하나라서 대상 선정 자체가 실험 설계였다.

조치 — 스냅샷 쿼리에 상시 포함

이 COUNT(*) 확인을 일회성으로 두지 않았다.

pending_same_address 를 스냅샷 쿼리에 상시 포함해서 카나리를 돌릴 때마다 자동으로 확인되게 했다.

사람의 기억에 의존하는 확인 항목은 급할 때 가장 먼저 빠진다. 절차에 넣어 두면 다음에 누가 하더라도 그 값이 함께 나온다.

사전 준비 — 스냅샷이 되돌림 근거

조작 전 값을 남기는 것까지가 절차다.

조작 후 이상함
    ↓
원래 값이 뭐였지?
    ↓
스냅샷에 있다

SELECT 로 떠 둔 스냅샷이 없으면 되돌릴 근거가 없다. 체인에 나간 것은 되돌릴 수 없어도 우리 쪽 상태와 대조할 근거는 남는다.

무엇을 담을지도 정리해 뒀다.

출금 행:  ID, 코인, 상태, nonce, txid, gas price, 전파 시각
회원:     member_no, 잔액 잠금액
노드:     주소의 확정 nonce, mempool 대기 nonce
파급:     같은 주소의 미확정 대기 건 수

SELECT 하나로 네 가지를 다 뽑는다.

토큰 출금은 결과 확인도 ETH 와 다르다. 컨트랙트의 transfer 호출이라 트랜잭션의 value 는 0 이고, EIP-20 명세가 transfer 는 반드시 Transfer 이벤트를 발생시켜야 한다고 정하므로 그 이벤트 로그에서 수량을 읽어야 한다. 스냅샷에 토큰 여부를 같이 적어 둔 것은 그래서다.

여러 번 나눠 뽑으면 그 사이에 값이 바뀔 수 있고 빠뜨리기도 한다. 조작보다 그 앞에 남기는 기록이 더 중요한 경우가 자주 있었다.

정리


Share this post on:

Previous Post
뒤에서 도는 것이 한도를 다 써 버렸다
Next Post
상태를 바꾸는 게터