본문으로 건너뛰기
Bankware Global Engineering

Distributed systems, ledger design, financial infrastructure, and protocol engineering.

모든 저자 보기

네 노드 중 하나만 멈췄다: 타임아웃 이후의 합의

· 약 8분
Bankware Global Engineering

네 대의 검증 노드가 하나의 원장을 만들고 있었다. 세 대는 첫 블록을 확정했는데, 한 대만 그 이전에 남았다. 뒤처진 노드의 프로세스는 살아 있었고 나머지 세 대와 연결도 유지했다. 그런데 기다려도 같은 블록에 도달하지 않았다.

2026년 9월 8일 nigo-protocol의 로컬 재현에서 확인한 장면이다. 저장소에서 블록을 읽지 못한 것도, 네트워크 연결이 끊긴 것도 아니었다. 뒤처진 노드는 오히려 합의 시도 횟수에서는 더 앞서 있었다. 먼저 타임아웃을 겪어 다음 라운드로 넘어간 뒤, 이전 라운드에서 온 메시지를 버리고 있었다.

시간이 지난 메시지를 거부하는 규칙은 자연스러워 보인다. 하지만 그 메시지가 새 투표를 요청하는 것이 아니라 다른 노드들이 이미 합의했다는 증거라면 어떨까? 이 차이가 살아 있는 노드 한 대를 계속 뒤에 남겨 두었다.

저장 함수가 돌아오면 서명을 보내도 될까

· 약 7분
Bankware Global Engineering

합의의 진행 상태를 관측하는 글에서 살펴봤듯, 서버가 응답한다는 사실과 거래를 확정하고 있다는 사실은 다르다. 합의가 어디서 기다리는지 관측하기 시작하면, 다음 질문은 기다림을 언제 끝내도 되는가로 옮겨간다. 특히 저장을 기다리는 중이라면, 빨리 다음 단계로 넘어가는 것이 항상 좋은 일은 아니다.

합의 노드는 자신이 지지하는 블록에 서명한 메시지를 다른 노드에 보낸다. 다른 노드는 그 서명을 투표로 받아들인다. 그런데 보낸 쪽이 갑자기 종료됐다가 돌아왔을 때, 이미 보낸 투표를 자신만 기억하지 못한다면 어떻게 될까?

nigo-protocol은 이를 막기 위해 외부로 전파할 합의 행동을 먼저 로컬 저장소에 기록한다. 문제는 “저장 함수가 정상적으로 돌아왔다”는 말에 무엇이 포함되느냐였다. 2026년 9월의 구현·검증에서는 기록 요청, 데이터베이스의 commit, 저장 장치로의 동기화 요청 완료를 하나의 성공으로 뭉뚱그리지 않는 것이 핵심이었다.

HTTP는 응답하는데 합의는 멈춰 있다면

· 약 8분
Bankware Global Engineering

거래를 제출했는데 확정되지 않는다. 운영 화면은 열리고, 상태 조회도 성공한다. 합의 엔진에는 running=true가 표시된다. 서버가 응답하고 엔진도 실행 중이라면, 조금 더 기다리기만 하면 되는 걸까?

이 정보로는 아직 답할 수 없다. 할 일이 없어 기다리는 노드일 수도 있고, 다른 검증자의 표를 기다리거나 저장 작업 안에서 멈춘 노드일 수도 있다. 화면에 남은 값이 장애가 나기 전 마지막 응답일 가능성도 있다.

GC의 안전성과 진전에서는 자료를 보호하며 멈춘 결과와 정리를 끝낸 결과를 구분했다. 합의 관측에도 비슷한 질문이 생긴다. 실패가 보이지 않는다는 것과, 해야 할 일이 끝나고 있다는 것을 어떻게 구별할까?

nigo-protocol은 이 질문을 응답 여부 하나에 맡기지 않았다. 최소 실행 상태를 읽는 경로를 분리하고, 거래 대기·투표·로컬 확정을 각각 관측해 진행을 판단한다. 이 글은 그 설계와 2026년 9월의 로컬 검증에서 확인한 범위를 다룬다.

병렬화의 효과는 어디에서 사라졌을까

· 약 9분
Bankware Global Engineering

서로 독립적인 거래를 함께 실행하고, 앞 거래를 기다리던 일을 준비되는 대로 배정하면 계산 시간과 불필요한 대기를 줄일 수 있다. nigo-protocol은 순차 실행과 같은 결과를 지키면서 이 기회를 이용하려고 여러 작업 배정 방식을 검토했다.

그런데 정확하게 동작하는 병렬 엔진을 만들었다고 해서 노드의 전체 처리 시간이 같은 비율로 줄지는 않았다. 2026년 8월 7일의 한 실험에서 거래 1,000건의 블록 실행 시간은 약 18% 줄었지만, 측정한 로컬 처리 경로 전체는 약 2.4%만 줄었다.

실행 구간에서 얻은 이득은 어디로 간 것일까? 당시 기록을 다시 따라가면 세 가지를 구분해야 한다. 무엇을 측정했는지, 실행 단계에 어떤 일이 남아 있었는지, 그리고 실행 방식들을 어떻게 비교했는지다. 이 글은 그 실험을 해석한 회고이며, 현재 nigo-protocol의 처리량을 새로 측정한 결과는 아니다.

같은 거래 의존성, 세 가지 병렬 실행 방식

· 약 15분
Bankware Global Engineering

두 거래를 함께 실행했는데 하나는 금방 끝나고 다른 하나는 오래 걸린다고 하자. 먼저 끝난 거래의 결과만 있으면 시작할 수 있는 다음 거래가 있다. 그 거래도 느린 쪽이 끝날 때까지 기다려야 할까?

순차 실행과 같은 원장 결과를 지키는 조건만으로는 답이 정해지지 않는다. 필요한 결과가 준비됐다는 것과, 그 일을 실제 작업자에게 맡기는 것은 다른 결정이기 때문이다.

nigo-protocol은 거래 사이의 의존 관계를 DAG라는 그래프로 표현하고, 그 그래프를 실행하는 방법으로 WAVE, READY_QUEUE, WORK_FIRST_CONTINUATION을 구현했다. 이름을 외우기 전에, 같은 거래 묶음에서 어떤 기다림을 줄이려는지 살펴보자.

동시에 실행해도 같은 원장이 되려면

· 약 9분
Bankware Global Engineering

같은 원장 상태에서 실행할 거래와 그 순서가 정해졌다고 하자. 어떤 노드는 그 거래들을 하나씩 실행하고, 다른 노드는 여러 작업에 나누어 동시에 실행한다. 두 노드가 같은 원장을 만들려면 무엇이 같아야 할까?

동시에 시작한 작업은 끝나는 순서가 달라질 수 있다. 뒤에 놓인 거래가 먼저 끝났다는 이유로 원장에 먼저 반영된다면, 다른 노드와 거래 순서나 실행 결과가 달라질 수 있다. 반대로 앞 거래가 끝나야만 다음 거래를 시작한다면, 서로 무관한 계산도 함께 실행할 수 없다.

Native Program의 검증 구조에서는 거래가 사용할 상태와 제안하는 변경을 미리 드러내는 방식을 살펴봤다. 그 정보는 여러 거래 중 무엇을 함께 실행할 수 있는지 판단하는 데도 쓰인다. 반면 EVM 함수 호출에서는 실행 도중 상태 접근이 결정될 수 있다. 결과가 결정론적이라는 것과, 실행 전에 거래의 독립성을 판단할 수 있다는 것은 별개의 조건이다. 이번에는 계산이 끝나는 순서가 달라도, 정해진 순서대로 하나씩 실행했을 때와 같은 결과를 만드는 방법을 보자.

nigo-protocol의 EVM 호환: 기존 컨트랙트와 개발 도구를 이어 쓰는 방법

· 약 8분
Bankware Global Engineering

새로운 원장을 선택할 때는 그 위에서 사용할 코드와 도구도 함께 생각하게 된다. 이미 Solidity로 작성한 컨트랙트가 있고, 앱은 익숙한 라이브러리로 잔액을 읽고 거래에 서명하고 있다면, 그 개발 자산을 이어 쓸 수 있다는 것은 큰 이점이다.

nigo-protocol은 자체 원장 구조에 EVM 실행 경로를 두어 이 연결을 제공한다. 실제 SDK 샘플 검증에서는 동일한 Token 바이트코드와 ABI를 유지한 채 ethers·viem·Web3j로 배포·조회·서명 전송·거래 결과 확인까지 수행했다. 필요한 변경은 업무 규칙의 재작성 없이 SDK 설정과 거래 옵션에서 처리했다.

EVM 함수 호출과 상태 변경이 컨트랙트 실행을 원장에 반영하는 구조를 설명했다면, 이번 글은 그 구조가 개발자에게 주는 이점을 살펴본다. 기존 코드와 도구를 이어 쓰는 일이 토큰 전송 앱에서 어떻게 나타나는지 따라가 보자.

안전하게 멈춘 GC는 일을 끝낸 것일까

· 약 9분
Bankware Global Engineering

과거 상태를 증명하려면 그때의 자료를 보관해야 한다. 그렇다고 모든 검증 자료를 계속 쌓아두면, 원장이 처리하는 거래가 늘어날수록 저장소를 유지하는 부담도 커진다. 보존할 범위를 정했다면, 이제 필요 없어진 자료를 정리하는 일도 필요하다.

과거 상태와 증명에서는 자료를 제공할 수 없다는 답과 그때 기록이 없었다는 답을 구분했다. 이번에는 그 자료를 지우는 쪽에서 묻는다. 지우면 안 되는 자료를 보호했다면, 정리 기능은 제 역할을 다한 것일까?

nigo-protocol의 2026년 9월 18일 로컬 실험에서는 이 질문에 두 가지 답이 나왔다. 거래가 계속 들어오는 동안 삭제 기준이 바뀌자 GC는 안전하게 멈췄다. 하지만 그 부하 구간에서 끝까지 완료한 정리 주기는 없었다. 무엇을 지켜냈고, 무엇을 아직 해내지 못했는지 함께 읽어야 하는 결과다.

자산의 규칙을 바꿀 때 기존 승인은 어떻게 될까

· 약 11분
Bankware Global Engineering

A는 자산 100을 갖고 있었고, S에게 그중 30까지 사용할 수 있도록 승인했다. S가 20을 B에게 보낸 뒤에는 A에게 80, B에게 20이 남았다. S가 앞으로 사용할 수 있는 한도는 10이다. Native Program의 검증을 다룬 글에서는 자산의 이전과 한도 감소를 하나의 거래로 검사하는 과정을 살펴봤다.

이번에는 그 거래가 끝난 뒤, 자산의 사용 규칙을 검사하는 프로그램이 바뀐다고 하자. 새 프로그램도 S에게 남은 10을 사용할 수 있게 해줘야 할까? 한동안 다른 프로그램을 쓰다가 처음 프로그램으로 돌아온다면, 그때의 승인도 다시 유효해질까?

현재 nigo-protocol은 같은 프로그램의 실행 버전인 리비전을 바꿀 때 기존 상태를 유지하고, 다른 프로그램으로 교체할 때는 새 상태 영역을 연다. 모든 업그레이드에서 상태를 초기화하는 구조는 아니다. 그러나 기록을 유지했다고 사용자의 승인 의미까지 유지되는 것은 아니고, 기록을 새로 시작한다고 모든 사용 조건이 더 엄격해지는 것도 아니다.

규칙이 바뀔 때 기존 상태의 연속성을 어디까지 보장할 수 있는가? 이 글에서는 원장이 집행하는 상태의 경계와, 프로그램을 바꾸는 쪽이 검토해야 하는 업무 의미를 구분한다.

EVM: 함수 호출은 어떻게 같은 원장의 상태 변경이 될까?

· 약 8분
Bankware Global Engineering

A가 가진 토큰 100 중 20을 B에게 보내려 한다. A는 S에게 30까지 사용할 권한을 주었고, 이번 거래는 S가 보낸다. 끝나면 A에게 80, B에게 20이 남고, S가 더 사용할 수 있는 한도는 10이다.

이 결과를 거래에 미리 적는 방법이 있다. 사용할 기록과 새로 남길 기록을 준비한 뒤, 그 변경이 허용되는지 프로그램에 검사받는 것이다. Native Program의 변경안 검증에서 살펴본 방식이다.

그런데 일반적인 컨트랙트 호출에서는 S가 변경 후 잔액과 한도를 출력으로 적지 않는다. 토큰 컨트랙트에 transferFrom(A, B, 20)을 실행해 달라고 요청한다. 현재 값을 읽고 다음 값을 만드는 일은 노드에서 실행하는 코드가 맡는다.

구체적인 변경안을 보내지 않았는데도, 함수 호출은 어떻게 같은 원장의 상태 변경이 될까? nigo-protocol의 세 번째 실행 경로인 EVM을 호출 하나의 안쪽에서 살펴보자.