Skip to content
isdnetworks
Go back

상태에 따라 부를 API가 다르다

(수정됨)

위탁 지갑 연동에서 출금 취소 부분을 붙이고 있었다. 문서를 읽고 만들었는데 실행하면 오류가 계속 났다.

Table of contents

Open Table of contents

증상 — 같은 요청이 갈렸다

돌아오는 것은 이 코드였다.

INVALID_STATE

INVALID_STATE 가 어떤 건에서는 안 나고 어떤 건에서만 나서, 오류 자체보다 그 갈림이 문제였다.

거부된 건들을 모아 보니 우리 쪽 status 가 같았는데 무엇이 그 차이를 만드는지는 문서에 없었다.

원인 — 문답 자료에 있던 상태 규칙

문답 자료를 전수로 훑었다. 출금 관련만 스물 남짓이었다.

created (생성)        →  withdraw/cancel 가능
signing (서명 대기)   →  오류
broadcast (전파됨)    →  별도의 "withdraw/bump" 를 써야 함

취소가 created 에서만 허용되고 signing 에서는 withdraw/abort 라는 다른 창구를 불러야 했다. broadcast 뒤에는 취소가 아예 없고 수수료를 올려 대체하는 withdraw/bump 만 있었다.

표준 규격 문서에는 state 규칙이 있다고만 적혀 있었다. 어느 상태에서 어느 창구를 쓰는지도 그때 파라미터가 무엇인지도 없었다.

표준 문서는 정상 경로를 설명하고 문답 자료에는 실패 사례가 쌓인다. 둘을 다 읽어야 만들 수 있다는 것이 여기서도 같았다.

조치 — 분기와 명시적 예외

switch 로 상태별로 부를 창구를 나눴다.

switch ($withdrawal->custodyState) {
    case 'created':
        return $this->custody->call('withdraw/cancel', ['id' => $withdrawal->custodyId]);
    case 'signing':
        return $this->custody->call('withdraw/abort', ['id' => $withdrawal->custodyId]);
    default:
        throw new InvalidStateException($withdrawal->custodyState);
}

default 에서 InvalidStateException 을 던지므로 목록에 없는 상태가 조용히 지나가지 않는다. broadcast 를 취소 경로에 넣지 않은 것도 그래서다.

그러지 않으면 새 상태가 생겼을 때 아무 처리도 안 되고 그것을 모른 채 지나간다. 조용히 넘기는 대신 멈추는 쪽을 골랐다. 이유는 예상 못 한 값에서 멈추면 나중에 조사할 시간이 크게 줄어서다.

분기를 만들 때 나머지 경우를 어떻게 할지가 분기 자체만큼 중요했다.

주의 — 성공이 반영이 아니었다

문답 자료에서 다른 것도 나왔다.

API 성공  →  즉시 반영 아님

withdraw/abort 의 성공을 받아도 상태 조회에 반영되는 것은 다음 서명 라운드가 끝난 뒤라 몇 분이 걸린다.

보내고 나서 바로 확인하면 아직 signing 이라, 안 됐나 싶어 다시 보내면 두 번째 abort 가 INVALID_STATE 로 튕긴다.

확인 시점을 뒤로 미루고 cron 이 몇 번 다시 보게 고쳤는데 성공과 반영 사이의 시차를 전제로 넣어야 했다.

비교 — 이름이 비슷한 필드들

노드 쪽 조회에는 비슷한 이름이 둘 있었다.

walletconflicts       이 트랜잭션과 충돌한(대체한) txid 목록
bip125-replaceable    대체될 수 있는지 여부 (yes / no / unknown)

walletconflicts 는 결과이고 bip125-replaceable 은 가능 여부라 잘못 읽기 쉽다. Bitcoin Core 0.16 의 gettransaction 공식 문서는 bip125-replaceable 이 mempool 에 없는 미확인 트랜잭션에서는 unknown 일 수 있다고 적는데, 그 값을 no 로 읽으면 대체 가능한 건을 고정된 건으로 오판한다.

이름만으로 판단하면 반드시 한 번은 틀리므로, 실제 값을 몇 건 받아 우리 자료와 대조해 의미를 확정하고 표로 적어 뒀다.

수수료 인상 대체에는 부작용도 있었다.

처음         txid A 로 전파
bump 후      txid B 로 대체, A 는 walletconflicts 에 남음

대체된 트랜잭션의 txid 가 바뀌므로 txid A 로 추적하면 못 찾는다.

제약 — 되돌릴 수 없는 처리와 완료 불가

가장 무거운 것은 이것이었다.

일단 broadcast 가 되면 취소로 못 돌아가고, 실수로 서명 라운드를 넘기면 그 건은 나가는 것으로 고정된다.

그래서 그런 처리 앞에는 확인 단계를 두고 대상 목록을 먼저 보게 했다.

내부 주소로 등록  →  confirmed 로 완료 처리 불가
                  ↓
              온체인 없이 settled 로 종료

위탁 지갑 안의 내부 주소로 보낸 출금은 온체인 트랜잭션이 없어 confirmed 가 영영 안 되는 것도 있었다.

confirmed 는 안 되는데 잔액은 이동하니, 이것을 모르면 완료가 안 된다고 계속 시도하게 되고 그 시도는 영원히 성공하지 않는다.

구현 전에 알아 둔 것을 목록으로 적어 뒀다.

① 상태별로 API 가 다르다        →  분기 필요
② 성공이 즉시 반영이 아니다      →  확인 시점 조정
③ 식별자가 변경된다             →  추적 방식 변경
④ 비가역 처리가 있다            →  사전 확인
⑤ 특정 유형은 완료 불가         →  예외 처리

다섯 다 오류를 만나 가며 알아내려면 며칠이 걸린다. 문답 자료 수백 건 중 출금 섹션 스물 남짓만 읽는 데는 반나절이면 됐다.

정리


Share this post on:

Previous Post
큐 테이블에서 워커끼리 서로 기다렸다
Next Post
저장소마다 커밋 작성자가 달랐다