ZYCORD 문서
한국어
Zycord문서노드 운영

노드 운영

zycordd를 위한 운영자 안내서입니다. 이 프로그램이 말하는 프로토콜은 와이어 명세이며, 이 페이지는 그것을 실행하는 방법에 관한 것입니다.

한 줄 요약#

zycordd --devnet --dir ./devnet

이것이 풀 노드입니다. 제네시스 블록부터 모든 블록을 검증하고, 피어에게 서비스하며, 127.0.0.1:9420에서 읽기 전용 RPC에 답합니다. 실제로 돌아가고 있는 네트워크에 참여하려면 공개 testnet을 보세요 — 그리고 거기에는 -randomx 바이너리가 필요하다는 점에 유의하세요.

노드가 갖고 있지 않은 것#

노드 프로세스에는 어떤 순간에도 키 자료가 없습니다. 노드는 시드도, 암호구도, 키 파일도 받지 않습니다. 서명은 zcd에서 이루어지며, 노드의 유일한 쓰기 엔드포인트 — /submit — 는 아무 권한도 부여하지 않습니다. 제출된 인증서(certificate)는 낯선 사람에게서 도착한 것과 정확히 똑같이 검증됩니다.

특권 엔드포인트도 없는데, 특권을 줄 대상이 없기 때문입니다. 어떤 키도 일시 정지하거나, 업그레이드하거나, 검열하거나, 발행할 수 없으므로, 노출할 호출 자체가 없습니다.

RPC를 프록시 뒤에 둔다면, 속도 제한은 프록시에서 거세요

이 제한기는 전송 계층의 피어를 기준으로 삼으며 X-Forwarded-For는 결코 쓰지 않습니다. 클라이언트가 설정하는 헤더를 신뢰하는 제한기는 클라이언트가 꺼 버릴 수 있는 제한기입니다. 그래서 프록시 뒤에서는 모든 요청이 프록시의 주소를 달고 도착하고, 클라이언트별 제한은 더 이상 클라이언트별이 아니라 모두가 한꺼번에 공유하는 분당 600건짜리 통 하나가 됩니다 — 그리고 호출자 하나가 모두를 위해 그 통을 비워 버릴 수 있습니다. 이는 제한이 아예 없는 것보다 나쁘다고 볼 만하며, 응답에는 그런 사정이 전혀 드러나지 않습니다. 호출자는 자기가 원인이든 다른 누가 원인이든 밋밋한 429를 받을 뿐입니다.

읽기 전용 표면#

체인을 바깥에서 지켜보는 모든 것 — 익스플로러, 모니터, 인덱서 — 은 여기서 체인을 읽습니다. 노드는 바이트를 건네줄 뿐 해석은 결코 건네주지 않습니다.

엔드포인트무엇에 답하는가
/status, /head팁의 식별자, 높이, 상태 루트
/block?height=N 또는 ?id=0x…블록 하나를 JSON으로, canonical, orphaned, confirmations와 함께
/block?…&format=ssz같은 블록을 정규 SSZ 바이트로, application/octet-stream
/params활성 파라미터 집합과 그 합의 루트
/cell, /balance팁 상태
/fees기본 수수료와 현재 적용 중인 탄력적 상한
/mempool풀 카운터. ?limit=N은 대기 중인 id를 최대 1000개까지, 낮은 것부터 덧붙입니다
/network, /metrics연결 형태와 카운터
/submit유일한 쓰기이며, 아무것도 부여하지 않습니다
상한은 파라미터가 아닙니다

백서 §8.1은 블록의 바이트, gas, 인증서(certificate) 상한을 순차 목표값인 T의 함수로 정하며, T는 에포크 컨트롤러가 움직이는 합의 상태입니다. 따라서 /params는 제네시스 값들만 담고 있습니다 — block_byte_limit_genesis와 그 동료들 말이며 — 이는 T가 출발하는 지점이자 되돌아가 감쇠할 수 있는 하한이지 현재 적용 중인 상한이 결코 아닙니다. 그 숫자들은 영원히 실재하는 값이므로, "블록 크기 제한"이라며 그중 하나를 읽으면 소리 없이 틀리게 되며, 그 오차는 제네시스 값 2.5 MB와 8 MB 용량 벽 사이의 거리만큼까지 벌어질 수 있습니다. /fees는 살아 있는 T와 거기서 유도된 네 개의 상한을 제공하므로, 유도 과정을 신뢰하는 대신 확인할 수 있습니다.

왜 바이트가 중요한가#

blake3("zcd/block/v1" ‖ header_bytes)가 블록 id이므로, 자신이 받은 것에서 id를 다시 유도해 보는 관찰자는 네트워크와 일치하거나 즉시 그렇지 않음을 알게 됩니다. 인증서(certificate)별 결과를 얻는 방법도 이것입니다. 어떤 인증서가 적용되었는지, 건너뜀(skip) 처리되고 청구되었는지, 폐기(drop)되었는지는 폴드(fold) 안에서 계산되며 결코 영속화되지 않습니다. 메모리에 사는 저장소에서 인증서마다 한 행씩 영원히 갖고 있으라는 것은 노드에게 요구할 수 있는 일이 아니기 때문입니다. 결과를 원하는 관찰자는 블록을 직접 폴드(fold)하면 됩니다.

여기 없고 앞으로도 없을 것#

  • 임의의 상태 순회는 없습니다. 주소 목록도, 접두사 스캔도, 부자 순위도 없습니다.
  • 과거 상태는 없습니다. /cell과 /balance는 팁을 읽습니다. "높이 H에서의 잔액"은 노드가 보관하지 않는 이력을 전제로 합니다.
  • 관리, 재색인, 디버그 호출은 없습니다. 특권을 줄 대상이 없기 때문입니다.

집계는 지켜보는 쪽의 몫이며, 자기 데이터베이스로 수집할 때 한 번 계산하면 됩니다.

상태 코드#

읽기는 GET과 HEAD에 답하고 그 밖의 모든 메서드는 405로 거부합니다. 형식은 올바른데 답이 부정적인 질문 — 체인이 아직 도달하지 않은 높이, 알 수 없는 id, 재구성에서 밀려난 블록의 본문 — 은 404이며, 400은 형식이 잘못된 요청에만 씁니다. 노드가 기록해 놓고 더 이상 읽어 낼 수 없는 레코드는 400이 아니라 500입니다. 잘못은 노드의 디스크에 있고, 호출자에게 요청 형식이 잘못되었다고 말하면 처음부터 형식이 올바랐던 요청을 재시도하지 않게 만들기 때문입니다. 폴러가 부재와 오류를, 또는 자기 버그와 노드의 버그를 구분하려고 산문을 읽어야 해서는 결코 안 됩니다.

재구성#

높이 조회는 정규 체인에 대해서만 답합니다. 재구성에서 밀려난 블록은 헤더를 유지하고 본문을 잃으므로, 그 id는 여전히 해석되고 orphaned로 표시되어 돌아오며, 분기점까지의 부모 링크도 여전히 갖고 있습니다 — 그렇기 때문에 id 조회가 재구성에 안전한 유일한 경로입니다.

외부 접속 가능성, 그리고 왜 보기보다 더 중요한가#

Zycord는 NAT 통과를 하지 않습니다. 도달 가능한 포트에서 --listen을 켠 노드는 다른 사람이 부트스트랩할 수 있는 출발점이 되며, 그런 노드의 비율은 NAT 통과를 하지 않겠다는 결정 전체가 딛고 선 반증 가능한 조건입니다.

# periphery: nothing to configure
zycordd --testnet --dir ./testnet

# core: bind a port, and make sure it is actually reachable
zycordd --testnet --dir ./testnet \
  --listen 0.0.0.0:9421 --advertise <public-address>:9421
응답하는 주소를 알리거나, 아무것도 알리지 마세요

--advertise는 --listen 값으로 되돌아가므로, --advertise 없이 --listen 0.0.0.0:9421을 쓰면 아무도 접속할 수 없는 0.0.0.0:9421을 공개하게 됩니다. 잘못된 주소는 피어 교환을 통해 퍼져 나가며, 그 주소로 시도하는 모든 노드에게 접속 시도 한 번씩의 비용을 물립니다. 포트 포워딩을 할 수 없다면 --listen을 끄고 주변부로 남으세요.

제대로 되었는지 확인하기#

curl -s localhost:9420/network
{"enabled":true,"peers":8,"listening":true,"inbound":5,"outbound":3,"reachable":true}

노드가 몇 분 동안 올라와 있는데도 listening: true인데 inbound: 0이라면, 포트가 실제로는 도달 불가능하다는 뜻입니다. 프로세스는 바인드된 채 기다리고 있는데 아무것도 도착하지 않는 것입니다. 다른 어떤 화면에서도 건강해 보이는 상태이며, 이 엔드포인트가 존재하는 이유가 그것입니다.

파일에서 부트스트랩 주소 읽기#

zycordd --testnet --dir ./testnet --peers-file peers.txt

한 줄에 주소 하나씩 적습니다. # 주석과 빈 줄은 무시되며, 이 목록은 네트워크에 내장된 시드와 합쳐집니다. --peers는 같은 것을 쉼표로 구분된 인자로 받고, --no-seeds는 둘을 유지한 채 내장 시드만 뺍니다.

ssh 터널을 통한 지갑 인터페이스#

노드는 키를 갖고 있지 않으며 앞으로도 갖지 않습니다. 지갑은 별개의 프로세스이고, 서버에서는 zcd ui입니다.

zcd ui --key wallet.json --no-open

이 명령은 URL을 출력하고 127.0.0.1:9430에서 서비스합니다. 루프백에 바인드하며 그 밖의 어떤 것도 거부하고, 이를 덮어쓸 플래그는 없습니다. 그 리스너 뒤의 프로세스는 잠금이 풀린 개인 키를 보유하고 URL에 담긴 베어러 토큰으로 인증하는데, 이는 로컬 컴퓨터만 열 수 있는 소켓에는 충분하고 다른 어떤 것에도 충분하지 않습니다. 다른 곳에서 접근하는 것은 ssh의 일이며, ssh는 이 프로그램이 할 수 있는 것보다 그 일을 더 잘합니다.

# on your own machine
ssh -L 9430:127.0.0.1:9430 <host>

그런 다음 서버가 출력한 URL을 여세요. 토큰은 URL의 프래그먼트에 실려 갑니다. 그래서 토큰은 서버에도, 로그에도, Referer 헤더에도 결코 도달하지 않습니다 — 그 URL은 실제로 비밀이니 비밀처럼 다루세요. 토큰은 실행할 때마다 새로 만들어지고, Ctrl-C는 키를 지우고 서비스를 멈춥니다.

포워딩의 로컬 쪽 포트가 반드시 9430일 필요는 없습니다. 이 인터페이스는 Host 헤더의 호스트 이름을 검사하며 포트는 의도적으로 검사하지 않습니다. 중요한 것은 호스트 이름입니다 — DNS 리바인딩을 막는 것이 그것이며, 루프백에 있는 서버에 대한 진짜 공격이 바로 그것입니다. 브라우저의 어떤 페이지든 127.0.0.1로 요청을 보낼 수 있지만, 그 페이지가 위조할 수 없는 단 하나는 자기가 어떤 이름으로 이동해 왔는가입니다.

zcd ui 앞에 리버스 프록시를 두지 마세요

노드의 RPC에 관한 위의 조언은 키를 갖고 있지 않고 아무 권한도 부여하지 않는 표면에 관한 것이었습니다. 이쪽은 키를 갖고 있습니다. 이것을 외부에 노출하는 방식 중 좋은 생각인 것은 하나도 없으며, 터널을 쓰는 데는 플래그 하나면 됩니다.

알아 둘 만한 다른 두 가지 형태가 있습니다.

  • zcd ui --locked는 터미널에서 암호구를 묻지 않고 시작하며, 대신 브라우저가 묻습니다. 터미널이 공유되거나 기록되는 환경에서 유용합니다.
  • zcd ui --lock-after 5m은 유휴 잠금 시간을 줄입니다. 이 잠금은 키를 제자리에서 지웁니다 — 시드는 참조만 끊기는 것이 아니라 덮어써집니다.

피어 신원과 익명성#

노드는 시작할 때마다 새 Ed25519 피어 키를 생성하며 디스크에 결코 기록하지 않습니다. 이 키는 어디에서도 유도되지 않습니다 — 여러분의 지갑에서도, 시드에서도, 그 컴퓨터의 다른 무엇에서도 아닙니다. 재시작하면 키가 교체됩니다.

여러분에게 중요한 방식으로 노드를 운영하고 있다면, 여러분의 익명성을 벗기는 것은 키가 아닙니다. 주소입니다. 여러분에게로 추적되는 인프라에서 운영하는 부트스트랩 노드는 아무리 키를 위생적으로 관리해도 고쳐지지 않는 익명성 해제 경로입니다.

데이터 디렉터리#

data/
  chain/       blocks, state, and the write-ahead log
  peers.json   the persisted peer store

피어 저장소는 의도적으로 영속화됩니다. 재시작할 때마다 백지 상태에서 시작하는 노드는 공격자에게 저장소를 채울 새 기회를 건네줍니다. 저장소를 삭제해도 안전하지만, 그 이점을 버리는 셈입니다.

노드는 어느 순간에 죽임을 당해도 살아남습니다 — 선행 기록 로그가 존재하는 이유가 그것이며, 네트워크 카오스 상황에서 노드를 무작위로 죽이는 방식으로 시험됩니다. 그러나 데이터 디렉터리가 노드 밑에서 편집되는 것에는 살아남지 못합니다. 시작할 때 상태 루트를 다시 계산하고, 저장된 값과 어긋나면 실행을 거부합니다.

네트워크 고르기#

플래그네트워크체인 id엔진
(없음)mainnet1randomx-v1
--testnet공개 testnet2randomx-v1
--devnet로컬 devnet1337개발용 엔진

파라미터는 경로에서 읽히는 것이 아니라 바이너리에 내장되어 있습니다. 공개 네트워크의 모든 참여자는 같은 바이트를 지녀야 하며, 그렇지 않으면 하나의 네트워크에 있는 것이 아닙니다. 그리고 --params로 넘기는 파일은 어긋나기 마련인 파일입니다. 다른 네트워크는 다른 네트워크입니다. 둘은 핸드셰이크에서 서로 연결을 끊고, 잘못된 체인을 담고 있는 데이터 디렉터리는 조용히 섞이는 대신 시작 단계에서 거부됩니다.

손상된 데이터 디렉터리 복구하기#

zycordd에는 노드가 그것을 상대로 기동을 거부하는 데이터 디렉터리를 위한 복구 경로가 들어 있습니다. 먼저 노드를 멈추세요 — zycordd repair --dir ./data는 디렉터리 잠금을 가져가며, 노드가 그 잠금을 쥐고 있는 동안에는 실행을 거부합니다 — 그런 다음 무엇이든 바꾸기 전에 --dry-run으로 무슨 일이 있었는지 물어보세요. 그 자리에서 복구되는 손상 유형은 하나뿐이고, 나머지의 해법은 재동기화이며, 어느 쪽인지 알려 주는 것이 바로 이 시험 실행입니다.