작업 중

작업 중: MainNet에 앞서 이 사이트를 구축하고 있습니다. 세부 내용은 변경될 수 있습니다.

이 사이트는 아직 구축 중입니다

ZooBC는 공개적으로 개발되고 있습니다. 여기서 보시는 내용은 최신이며 정직하지만, 완성된 것은 아닙니다. 최종 입장이 아니라 현재의 상태로 읽어 주세요.

MainNet까지 문구, 구조, 이미지, 수치 등 많은 것이 바뀔 것입니다. 일부 페이지는 임시로 채워 둔 상태입니다.

더 넓은 ZooBC 생태계는 단계적으로 도입됩니다. 지갑, 익스플로러, 커뮤니티 채널은 MainNet이 다가오면서 하나씩 열리며, 이 사이트도 함께 성장합니다.

내용이 잘못되었거나, 깨져 보이거나, 오해의 소지가 있는 부분이 있으면 알려 주세요. 지금의 피드백이 나중의 매끄러운 출시보다 저희에게 더 큰 가치가 있습니다.

ZOOBC / 개발자

개발자

프로그램이 이 체인을 읽고 쓰는 데 필요한 모든 것을 담았습니다. 게이트웨이는 HTTPS 정문입니다. TLS를 종료하고, 요청 속도를 제한하고, 체인 API를 노드로 프록시하므로 브라우저는 노드를 직접 운영하지 않고도 ZooBC와 통신할 수 있습니다.

어느 체인을 대상으로 개발하고 있나요?

아래 예제는 공개 TestNet 게이트웨이에서 실행됩니다. 주소, 잔액, 트랜잭션 해시는 네트워크 간에 이어지지 않으므로, 작업하기 전에 지금 어느 네트워크에 있는지 확인하세요.

TestNetzoobc.network
MainNetzoobc.net
체인 상태API 참조

MainNet은 2027년 2월 14일에 열립니다

하나의 기본 URL

체인에 관한 모든 것은 이 게이트웨이의 /api/v1/ 아래에 있으며, 이 페이지와 같은 출처에서 제공됩니다. 협상할 CORS도, 발급받을 키도, 요금제도 없습니다.

zoobc.network는 MainNet이 아니라 공개 ZooBC TestNet 게이트웨이입니다. 아래 예제는 테스트 네트워크에서 실행됩니다.

curl https://this-gateway/api/v1/blockchain/status

읽기와 쓰기는 서로 다른 곳으로 가며, 이는 생각보다 중요합니다:

GET아카이브 노드로 프록시됩니다. 아카이브 노드는 깊은 이력, 즉 어느 높이를 기준으로 하든 블록, 트랜잭션, 계정을 보관합니다.
POST아카이브 노드가 아니라 풀 노드로 곧장 프록시됩니다. 트랜잭션을 제출하려면 멤풀이 필요한데, 아카이브 노드는 읽기 전용 이력 서비스라서 예전에는 POST에 404로 응답했습니다. 한때 게이트웨이를 통한 브로드캐스트가 작동하지 않았던 이유입니다.

가장 먼저 찾게 될 경로

/api/v1/blockchain/status높이, 확정된 높이, 최신 블록
/api/v1/blocks/latest가장 최근 블록 전체
/api/v1/accounts/<address>잔액과 사용 가능 잔액. ZBC_… 주소나 36바이트 원시 hex를 받습니다
/api/v1/accounts/<address>/tokens해당 계정이 보유한 모든 토큰
/api/v1/transactions?limit=25최근 서명된 트랜잭션
/api/v1/movements/latest체인이 스스로 만드는 잔액 변동: 보상, 베스팅, 환불
/api/v1/exchange/markets열려 있는 마켓. 호가 깊이는 …/orderbook?market=<id>, 스왑 패키지는 …/offers
/api/v1/tokens공급량과 소수점 자릿수를 포함한 모든 토큰
/api/v1/release/list게시된 릴리스와 그 해시, /verify가 대조하는 기준
/api/v1/registry/nodes노드 레지스트리, 그리고 /gateways, /relays, /archivals

일부 경로는 아카이브 노드에만, 일부는 풀 노드에만 있습니다. 이 게이트웨이는 먼저 아카이브 노드를 시도하고 본문 없는 404가 오면 노드 API로 다시 시도하므로, 어느 쪽인지 신경 쓸 일은 거의 없습니다. 하지만 본문이 있는 404는 길을 잘못 든 것이 아니라 실제 답변입니다.

트랜잭션 제출

zbc-cli로 빌드하고 서명하세요. 이 도구는 매개변수를 stdin의 JSON으로 받으므로 개인 키가 ps나 셸 기록에 남지 않습니다:

echo '{"sender_privkey":"…","recipient":"ZBC_…","amount":100000000}' \
  | zbc-cli send --json-input --api https://this-gateway

1 ZBC는 108 아토믹 단위입니다. API의 모든 금액은 아토믹 단위이며, 전송 형식 어디에도 소수점은 없습니다.

이 기반 위에서 만들어지고 있는 것

여기서 호출하는 것과 같은 트랜잭션 유형이 현재 진행 중인 작업의 기반입니다:

AI 에이전트예약 이체, 에스크로, 멀티시그를 자율 에이전트가 스스로 올릴 수 없는 지출 상한으로 활용합니다. 지금 작동하며, 공개 TestNet을 대상으로 한 의존성 없는 Python 참조 구현이 있습니다.
탈중앙화 AI독립적으로 운영되는 모델 전문 시스템들을 사용자 측에서 조립하고 공개적으로 정산하여, 어떤 회사도 사람과 그가 사용하는 지능 사이에 끼어들지 않도록 합니다. MainNet 이후의 방향입니다.
ProxCell대면 결제를 위한 로컬 낙관적 확인과 그 위의 지리적 정산 계층. 워킹 페이퍼.

전체 참조

모든 경로와 매개변수, 응답 형식:

API 참조 열기 →  ·  다른 보기

두 주소 모두 같은 문서를 제공합니다.

다운로드

운영자가 이 게이트웨이에 게시한 파일은 /dl/<name>에서 제공됩니다. 이름은 [A-Za-z0-9._-]의 단일 세그먼트이며, 모든 파일은 렌더링되지 않고 항상 첨부 파일로 전송됩니다. 이 출처는 지갑도 제공하므로, 여기서 렌더링된 페이지는 지갑의 출처를 물려받게 되기 때문입니다.

릴리스 번들은 /releases/<version>/<arch>/ 아래에 있으며, /releases/latest가 현재 번들을 가리킵니다. 노드 설치 프로그램 위치: /install.

이 게이트웨이는 정상인가요? 확인 중…

/gateway/status 이 게이트웨이가 자신과 자신이 접근할 수 있는 노드에 대해 알고 있는 정보, 즉 모드, 도메인, 허용 목록의 노드 수, 응답 중인 노드를 보고합니다.

/gateway/system-stats 머신 정보, 즉 CPU, 메모리, 디스크, 가동 시간을 보고합니다. 게이트웨이 페이지의 수치는 여기서 가져옵니다.

curl https://this-gateway/gateway/status
curl https://this-gateway/gateway/system-stats

둘 다 키가 필요 없습니다. 둘 다 체인에 관한 정보는 노출하지 않으므로, 체인 정보가 필요하면 위의 /api/v1/…를 사용하세요.

특정 노드에 접근하기

체인 경로는 이 게이트웨이가 고른 아카이브 노드가 응답합니다. 특정 노드에 질의하려면 호스트 이름에 그 노드의 IP를 넣으세요. 점은 대시로 바뀌며, 선택 사항인 -p<port>로 API 포트를 지정합니다:

curl https://ip-192-168-1-100.this-gateway/api/v1/node/info
curl https://ip-192-168-1-100-p8080.this-gateway/api/v1/node/info

이 게이트웨이의 허용 목록에 있는 노드만 응답합니다. 그 밖의 대상은 프록시되지 않고 거부되므로, 호스트 이름을 이용해 게이트웨이를 거쳐 임의의 호스트에 접근할 수 없습니다.

릴레이

음성, 영상, 화면 공유는 P2P로 이루어집니다. 방화벽이 직접 연결을 막으면 릴레이가 크레딧을 받고 트래픽을 전달합니다. /relay/info는 릴레이의 주소, 요금, 정산하는 체인의 제네시스 해시를 보고하며, 지갑은 지불하기 전에 이를 자신의 노드와 대조합니다. 다른 체인의 릴레이는 받은 대금을 크레딧으로 반영할 수 없기 때문입니다.

curl https://this-gateway/relay/info

관리자 패널

게이트웨이 자체의 제어 기능은 zoobc.network/admin에 있습니다. /etc/zoobc/gateway.json의 admin_api_key로 보호되며, 이 키는 설치 프로그램이 서버마다 생성합니다. 기본 키는 없습니다.

허용 목록

게이트웨이는 등록된 노드로만 프록시합니다. 이 목록이 있기에 ip-… 호스트 이름 형식이 게이트웨이를 개방형 프록시로 만들지 못합니다.

POST /gateway/allowlist/add노드 추가, {"ip":"…","port":8080}
POST /gateway/allowlist/remove노드 제거
POST /gateway/allowlist/refresh레지스트리를 다시 읽고 상태를 다시 확인
GET /gateway/allowlist현재 허용된 노드

세 가지 쓰기 작업 모두 관리자 키가 필요합니다. 공개 모드에서는 게이트웨이가 체인 자체의 레지스트리에서도 노드를 찾고, 비공개 모드에서는 다음만 사용합니다: static_nodes.

릴리스 도구

파일 해셔는 직접 확인하고 싶은 어떤 파일이든 브라우저에서 SHA-256을 계산하고, /verify는 내려받은 번들을 체인이 게시한 해시와 대조합니다.

두 문서

하드웨어 지갑 팀에 필요한 모든 것이 여기에 명시되어 있으며, 전체 내용은 아래 두 PDF에 있습니다. 두 문서 모두 ZooBC 노드 v0.4.0(7ad161a4 시점의 zoobc-main)과 실제 v0.4.0 노드가 수락한 트랜잭션 벡터로 검증되었습니다.

버전 2.1은 한 가지 실질적인 점에서 초안 1을 대체합니다. 이제 트랜잭션 서명이 체인에 바인딩됩니다. 초안 1을 기준으로 개발했다면 서명 다이제스트와 ApprovalEscrow 바디, 이 두 가지가 호환되지 않게 바뀌었습니다. 예전 v0.3.2 구성을 구현한 곳은 없으므로 현재 문서만을 기준으로 개발하세요.

한 페이지 요약

서명 방식Ed25519(RFC 8032, 순수 방식, 사전 해시 변형 없음, 컨텍스트 문자열 없음). 64바이트 서명, 32바이트 공개 키.
서명 대상Ed25519.sign(sk, SHA3-256("ZBC-TX" ‖ genesis_hash(32) ‖ unsigned_bytes)). 32바이트 다이제스트가 바로 Ed25519 메시지입니다.
체인 바인딩다이제스트는 체인의 제네시스 블록 해시를 포함하므로, 한 체인을 위해 만든 서명을 다른 체인에서 재사용(리플레이)할 수 없습니다. 전송 형식은 바뀌지 않으며, 제네시스 해시는 트랜잭션 필드가 아닙니다.
서명 태그"ZBC-TX", ASCII 6바이트 5a 42 43 2d 54 58. 종료 문자 없음, 길이 접두사 없음.
해시 함수SHA3-256(FIPS 202). Keccak-256도 SHA-256도 아닙니다.
키 파생BIP-39 니모닉에서 시드(PBKDF2-HMAC-SHA512, 2048회 반복)를 거쳐 SLIP-0010 Ed25519, 경로 m/44'/883'/account'. 강화(hardened) 레벨 3개, change 및 index 레벨 없음.
SLIP-44 코인 유형883 (등록됨: 883 | ZBC | ZooBC).
계정(전송 형식)36바이트: u32le(0) ‖ pubkey(32). JSON API는 hex 형식을 받습니다.
주소(표시용)ZBC_ 뒤에 base32 문자 56개(8자씩 7개 그룹). 3바이트 체크섬 = SHA3-256(pubkey ‖ "ZBC")[0..3].
버전 바이트0x01, 변경 없음. 체인 바인딩 때문에 올리지 않았으며, 버전 구분자가 아닙니다.
기본 단위1 ZBC = 100 000 000 아토믹 단위(소수점 8자리). 금액과 수수료는 u64 리틀 엔디언 아토믹 단위입니다.
바이트 순서전송되는 모든 데이터는 리틀 엔디언입니다. 단, SLIP-0010 자식 인덱스는 예외입니다(표준에 따라 빅 엔디언).
트랜잭션 해시tx_hash = SHA3-256(unsigned_bytes ‖ signature); tx_id = int64le(tx_hash[0..8]). 제네시스 해시는 서명 다이제스트에만 들어갑니다.
리플레이 방지체인 바인딩, 해시 고유성, 그리고 3600초 포함 기간. 논스는 없습니다.
최소 수수료기본 0.025 ZBC(2 500 000 아토믹 단위)에 네트워크 수수료 배율을 곱하고, 크기에 따른 임대료를 더합니다. 노드에 문의하세요: GET /api/v1/blockchain/estimate-fee.
1티어 유형1 SendZBC, 11 TransferToken, 4 ApprovalEscrow.
체인 탐색GET /api/v1/node/info genesis_hash, signing_version: 2, signing_tag: "ZBC-TX"를 반환합니다. 브로드캐스트할 엔드포인트에서 읽으세요.

이 네 가지를 먼저 읽으세요

구현에 다른 무엇보다 큰 영향을 주는 네 가지 사항.

1논스가 없으며, 서명되지 않은 트랜잭션을 만들어 주는 엔드포인트도 없습니다. ZooBC는 계정 기반이지만, 여기서 계정 기반이라고 해서 둘 중 어느 것이 있다는 뜻은 아닙니다. 트랜잭션은 호스트가 직접 직렬화합니다. 아래 레이아웃은 완전하며 테스트 벡터로 고정되어 있습니다.
2서명은 태그가 붙은 접두사를 포함합니다. 트랜잭션 바이트만이 아니라 "ZBC-TX"와 체인의 제네시스 해시까지 포함합니다. 이를 잘못 처리하면 모든 서명이 일반적인 오류로 실패합니다.
3브로드캐스트 엔드포인트는 서명된 트랜잭션 블롭을 받지 않습니다. 트랜잭션 바디 바이트와 엔벨로프를 이름 있는 JSON 키로 받아, 엔벨로프를 직접 재구성합니다.
4이력은 최신순으로 반환되며, limit로 범위가 제한됩니다. 두 엔드포인트를 함께 쓰면 전체 그림을 얻을 수 있으며, 금액은 엔벨로프 필드가 아니라 유형에 따라 달라집니다.

키 파생과 주소

참조: zoobc-signer/extension/lib/zoobc-crypto.js, 참조 지갑과 바이트 단위로 동일합니다. 노드 측 대응 구현 위치: src/crypto/slip10.cpp.

seed = PBKDF2-HMAC-SHA512(NFKD(mnemonic), "mnemonic" + passphrase, 2048, 64)
I    = HMAC-SHA512("ed25519 seed", seed);  k = I[0..32], c = I[32..64]

for index in [44, 883, account]:                       # all hardened
    data = 0x00 || k || u32be(index + 0x80000000)
    I    = HMAC-SHA512(c, data);  k = I[0..32], c = I[32..64]

pubkey = Ed25519.publicKeyFromSeed(k)                  # k is the 32-byte seed

계정 0은 m/44'/883'/0', 계정 1은 m/44'/883'/1', 이런 식으로 이어집니다. 키 파생은 체인 바인딩의 영향을 받지 않습니다. 하나의 키 세트가 모든 ZooBC 체인에 쓰이며, 달라지는 것은 서명뿐입니다.

표시용 주소는 36바이트 계정이 아니라 공개 키로 만듭니다:

buf[35] = pubkey(32) || "ZBC"
h       = SHA3-256(buf);  buf[32..35] = h[0..3]        # 3-byte checksum
s       = base32(buf)                                   # RFC 4648 A-Z2-7, no padding, 56 chars
address = "ZBC_" + s[0..8] + "_" + s[8..16] + ...       # 7 groups, canonical length 66

디코딩 시 밑줄, 대시, 공백 또는 구분자 없음을 모두 허용하며 대소문자도 구분하지 않습니다. 체크섬은 다시 계산해 비교해야 합니다. 위의 3바이트 SHA3 체크섬이 코드와 체인이 사용하는 정의이므로, 이 정의와 명세서의 참조 구현(예시 주소를 그대로 재현합니다)을 바탕으로 구현하세요.

서명되지 않은 트랜잭션의 전송 형식

기준 소스: src/util/transaction_util.cpp:144-270. 전송 형식은 v0.4.0에서 바뀌지 않았습니다. 아래의 길이나 오프셋 중 체인 바인딩의 영향을 받는 것은 없습니다.

#크기필드비고
14transaction_typeu32le, 아래 유형 목록 참조
21버전항상 0x01. 노드는 다른 값을 모두 거부합니다
38타임스탬프u64le, Unix 초, 0보다 큼
436발신자00000000 ‖ ZBC 서명자의 경우 pubkey(32)
54 또는 4+n수신자ZBC의 경우 36바이트, 그 밖에는 u32le(type) ‖ payload, 수신자가 없으면 02 00 00 00만
68수수료u64le, 아토믹 단위
74body_lengthu32le
8n바디유형별 레이아웃
94 또는 가변에스크로에스크로 없음: 4바이트 02 00 00 00. 에스크로 있음: approver(36) ‖ commission u64le ‖ timeout u64le ‖ instr_len u32le ‖ instruction ‖ multi_party(1)
104message_lengthu32le
11m메시지일반 텍스트, 일반 트랜잭션의 경우 256바이트 이하

에스크로 블록 끝의 multi_party 바이트는 필수입니다. 이를 생략하거나 에스크로 없음 표시를 다른 값으로 쓰면, 노드에서 다른 진단 정보 없이 "Invalid transaction signature"만 반환됩니다.

체인에서 검증된 실제 예제

자기 자신에게 1 ZBC를 보내는 SendZBC, 수수료 0.05 ZBC, 타임스탬프 1700000000, 에스크로 없음, 메시지 없음. 명세서에 있는 벡터 7개 중 벡터 0입니다.

01000000                 type = 1 (SendZBC)
01                       version
00f1536500000000         timestamp 1700000000
00000000 5e8eb28d...2152 sender    (type 0 + pubkey)
00000000 5e8eb28d...2152 recipient (type 0 + pubkey)
404b4c0000000000         fee 5 000 000
08000000                 body length 8
00e1f50500000000         body: amount 100 000 000
02000000                 no-escrow marker
00000000                 message length 0

총 113바이트입니다. 서명 프리이미지는 그 앞에 38바이트를 더 붙입니다:

5a42432d5458              tag "ZBC-TX"          6 bytes
090ab3c7...a61878         genesis hash         32 bytes
0100000001...00000000     the 113 bytes above 113 bytes
                                              ---------
                                              151 bytes

digest = SHA3-256(preimage) = f37e5340...4644fc, 서명 9a3b044d...03bb07, tx_hash = f00a6692...ce78b0. 실제 v0.4.0 노드가 HTTP 202와 정확히 이 해시를 반환했습니다.

서명 절차(기기 측)

input : account_index, genesis_hash(32), unsigned_bytes

1. k      = SLIP10(m/44'/883'/account_index')
2. pub    = Ed25519.pub(k);  self = 00000000 || pub
3. parse unsigned_bytes; every length must land exactly at the end
4. require version == 1 and sender == self
5. decode body per type; render the approval screen; wait for the user
6. digest = SHA3-256("ZBC-TX" || genesis_hash || unsigned_bytes)
7. sig    = Ed25519.sign(k, digest)          # the digest IS the message
8. return sig(64), and tx_hash = SHA3-256(unsigned_bytes || sig) for the host to cross-check

6단계에서 전체 트랜잭션을 RAM에 올리지 않아도 되도록 스트리밍 SHA3를 사용하세요. 태그 6바이트, 제네시스 32바이트, 그다음 트랜잭션 순서로 입력합니다.

명세서 전체에서 구현 오류가 가장 많이 나올 만한 부분이 바로 태그입니다. "ZBC-TX"는 ASCII 6바이트 5a 42 43 2d 54 58이며, NUL 종료 문자도 길이 접두사도 없습니다. 문자열에 종료 문자를 붙이거나 7바이트를 쓰는 펌웨어는 정상처럼 보이는 서명을 만들지만, 체인에서는 진단 정보 없이 일반적인 오류로 실패합니다. 무엇보다 먼저 벡터 0으로 테스트하세요.

체인 바인딩이 하는 일과 하지 않는 일

제네시스 32바이트는 호스트가 제공하며, 기기가 조회하지 않습니다. 따라서 서명은 체인에 구애받지 않으며, 하나의 코드 경로로 TestNet, devnet, MainNet은 물론 모든 비공개 또는 병렬 ZooBC 체인을 지원합니다.

하는 일체인 간 리플레이를 막습니다. 체인 A를 위해 만든 서명은 체인 B에서 검증되지 않습니다. 체인 바인딩은 바로 이것을 위해 만들어졌으며, 이 역할을 완벽하게 해냅니다.
하지 않는 일손상된 호스트가 잘못된 체인을 지정하는 것은 막지 못합니다. 기기는 전달받은 해시가 무엇이든 그에 맞춰 서명합니다.

따라서 알려진 제네시스 해시를 담은 펌웨어 테이블은 여전히 필요하지만, 용도는 하나뿐입니다. 화면에 신뢰할 수 있는 네트워크 이름을 표시하고, 해시가 테이블에 없으면 "UNKNOWN CHAIN"과 처음 8자리 hex를 대신 표시하는 것입니다.

인식되지 않는 체인을 기본적으로 거부하지 마세요. devnet의 해시는 다시 시작할 때마다 바뀌고 ZooBC에서는 비공개 또는 병렬 체인이 흔하므로, 무조건 거부하면 통합 작업에도 정당한 배포에도 기기를 쓸 수 없게 됩니다. 공급업체가 그런 잠금을 원한다면 기본값이 꺼짐인 명시적 사용자 설정으로 만드세요.

트랜잭션 유형: 표준 펌웨어가 지원해야 할 것

유형 id = group + 256 × subtype. 유형은 56가지가 있습니다. 이 세 가지로 ZBC 지불, 모든 토큰 지불, 에스크로 완료를 처리할 수 있으며, 바디는 작고 길이가 고정되어 있습니다.

id이름바디수신자
1SendZBCamount u64le (8)필수, 모든 유형
11TransferTokentoken_id i64le (8) ‖ amount i64le (8) ‖ 선택 사항 fee_in_token u8필수
4ApprovalEscrowapproval u32le ‖ escrowed_transaction_hash (32) = 36바이트빈 표시. 발신자가 승인자

token_id 는 해시에서 파생된 부호 있는 64비트 값이므로 음수일 수 있습니다. 제네시스 코인은 id 1부터 14까지이고, ZBC 자체는 id 0이며 TransferToken에는 절대 나타나지 않습니다. 각 토큰에는 기기가 검증할 수 없는 고유한 decimals(0부터 8)가 있습니다.

ApprovalEscrow는 v0.4.0에서 바뀌었습니다. 바디는 원래 approval와 8바이트 id(12바이트)였지만, 이제는 approval와 에스크로된 트랜잭션의 32바이트 해시(36바이트)이며, 파서는 다른 길이를 모두 거부합니다. 바로 하드웨어 지갑 때문에 바뀐 것입니다. 8바이트 id만 보는 서명자는 자신이 무엇을 해제하는지 확인할 수 없었고, 8바이트 id는 기기에 보여 줄 가짜 에스크로를 상대로 2^64 탐색을 하면 찾아낼 수 있습니다. 전체 해시가 있으면 기기는 자신이 승인하는 대상을 검증할 수 있습니다.

기기가 검증하는 것

구조11개 필드를 파싱합니다. 각 길이 접두사가 일관되어야 하며, 버퍼는 메시지 바로 뒤에서 정확히 끝나야 합니다. 그 밖의 경우는 거부합니다.
버전반드시 1이어야 합니다.
발신자요청된 경로에 대해 기기가 직접 파생한 계정과 같아야 합니다. 기기는 전달받은 발신자 바이트를 절대 신뢰하지 않으며, 직접 다시 파생해 36바이트 전체를 비교합니다. 이것이 "어디서 온 것인가"에 대한 보장의 전부입니다.
유형지원되는 유형이어야 하며, 그렇지 않으면 전문가 모드가 켜져 있어야 합니다.
바디 길이8 (SendZBC), 16 또는 17(TransferToken), 36(ApprovalEscrow). 추가 바이트가 있으면 거부합니다. 노드는 더 긴 SendZBC 바디를 받아들이고 뒷부분을 무시하는데, 숨겨진 페이로드가 끼어드는 방식이 바로 이것입니다.
에스크로 시간 제한트랜잭션 타임스탬프보다 커야 합니다.
제네시스 해시정확히 32바이트여야 합니다. 기기는 이를 목록과 대조해 검증하지 않지만, 길이가 틀리면 잘못된 형식의 요청입니다.
절대 금지미리 계산된 다이제스트를 받아 서명하는 것. 기기는 태그와 제네시스를 포함해 바이트를 직접 해싱합니다.
크기 상한권장값: 서명되지 않은 트랜잭션 2 KB 이하, 메시지 256 B, 에스크로 지시문 512 B이며, 이보다 크면 TOO_LARGE로 거부합니다. 1티어 트랜잭션은 300바이트 미만이지만, ApprovalEscrow는 검증을 위해 두 번째 트랜잭션이 첨부되어 오므로 상한은 두 버퍼를 모두 감당해야 합니다. 더 큰 페이로드는 소프트웨어 지갑에서 처리해야 합니다.

사용자가 화면에서 승인하는 내용

체인에서 검증된 참조 서명기를 바탕으로, 화면별로 다음 순서를 따릅니다.

네트워크제공된 제네시스 해시에 해당하는 펌웨어 테이블의 이름. 테이블에 없으면 "UNKNOWN CHAIN"과 처음 8자리 hex.
유형"Send ZBC"(ZBC 보내기), "Send token"(토큰 보내기), "Approve escrow"(에스크로 승인).
금액amount / 1e8 ZBC 기호와 함께 표시.
수신자전체 주소를 표시하며, 절대 생략하지 않습니다. 가운데를 생략한 주소로는 배니티 생성으로 만든 비슷한 모양의 키를 막을 수 없습니다.
수수료아토믹 단위 / 1e8 ZBC.
총 출금액금액과 수수료를 합한 하나의 숫자이며, ZBC 금액에만 해당합니다. 토큰 금액과 ZBC 수수료는 단위가 다르므로 더해서는 안 됩니다.
메시지출력 가능한 문자이면 그대로 표시하고, 그렇지 않으면 "N bytes (binary)"와 SHA3-256의 처음 8자리 hex를 표시합니다.
에스크로승인자 전체 주소, 커미션, 날짜와 시간으로 표시한 시간 제한, 지시문 텍스트.
토큰(유형 11)토큰 id(부호 있는 10진수)와 아토믹 금액. 호스트가 표시용 힌트로 decimals나 symbol를 제공하면, 기기가 이를 검증할 수 없으므로 "앱이 보고한 값"이라는 라벨과 함께 환산 금액을 표시합니다.
에스크로 승인큰 글씨로 "APPROVE" 또는 "REJECT", 그다음 검증된 에스크로 세부 정보, 그다음 수수료.

타임스탬프는 표시할 필요가 없습니다. 사용자에게 의미 있는 정보가 아니며, 포함 기간은 노드가 강제합니다.

호스트와 기기 간 프로토콜

전송 계층은 공급업체 자체의 USB/HID 계층과 기존 청크 분할 방식을 사용합니다. 중요한 것은 내용입니다.

명령요청응답
GET_APP_VERSION없음펌웨어 버전, 지원 유형 집합, 최대 tx 크기
GET_PUBLIC_KEYaccount_index, confirm_on_devicepubkey(32), address (66자 문자열). 확인 시 기기는 사용자가 웹사이트와 비교할 수 있도록 전체 주소를 표시합니다.
SIGN_TXaccount_index, genesis_hash(32), unsigned_tx_bytessignature(64), tx_hash(32), 또는 유형이 지정된 거부: SENDER_MISMATCH, UNSUPPORTED_TYPE, MALFORMED, USER_REJECTED, TOO_LARGE, ESCROW_HASH_MISMATCH
SIGN_MESSAGEaccount_index, message_bytessignature(64), 원시 Ed25519, 아래 규칙 참조

genesis_hash는 표시용 힌트가 아니라 결과를 좌우하는 값입니다. 초안 1에서는 네트워크 매개변수가 라벨만 고르는 용도였기 때문에 열거형이어도 됐습니다. 이제는 다이제스트 안에 들어가므로 실제 32바이트여야 하며, 잘못된 값을 넣으면 어떤 체인도 받아들이지 않는 서명이 만들어집니다.

원시 서명 규칙. 원시 서명 명령은 정확히 32바이트인 입력에 절대 서명해서는 안 되며, 텍스트로 표시할 수 있는 입력에만 서명해야 합니다. 32바이트 원시 입력은 사용자가 본 적 없는 트랜잭션의 SHA3 다이제스트일 수 있고, 그러면 메시지 명령이 내용을 보지 않고 하는 트랜잭션 서명으로 바뀝니다. 길이 32를 거부하면 체인을 변경하지 않고도 이 허점을 막을 수 있습니다.

전체 순서

Website (page)         Companion / host           device                    Gateway / node
   |-- connect ---------->|                            |                          |
   |                      |-- GET_PUBLIC_KEY(0) ----->|  (shows ZBC_... address) |
   |<-- address ----------|<-- pubkey, address -------|                          |
   |-- GET /api/v1/node/info  (genesis_hash, signing_version, height) ---------->|
   |-- GET /api/v1/accounts/<addr>  (balances) --------------------------------->|
   |-- GET /api/v1/accounts/<addr>/tokens, /api/v1/tokens/<id> (decimals) ------>|
   |   user fills the form                             |                          |
   |-- GET /api/v1/blockchain/estimate-fee?type=1&body_length=8 ---------------->|
   |<-- {minimum_fee, timestamp} ------------------------------------------------|
   |-- fields {type, recipient, body, fee, message} -->|                          |
   |                      |  serialize the 11 fields   |                          |
   |                      |-- SIGN_TX(0, genesis, -->|  parse, verify, display,  |
   |                      |            bytes)         |  user approves            |
   |                      |<-- signature, tx_hash ---|                          |
   |<-- signature --------|                            |                          |
   |-- POST /api/v1/transactions {JSON} ---------------------------------------->|
   |<-- 202 {status:"success", transaction_hash} --------------------------------|
   |-- GET /api/v1/transactions/<hash>/status  (staging, mempool, confirmed) --->|

호스트는 노드의 transaction_hash를 기기의 tx_hash와 비교합니다. 두 값은 같아야 하며, 다르면 호스트가 서명 후에 바이트를 변경한 것입니다.

브로드캐스트할 엔드포인트에서 genesis_hash와 signing_version를 읽으세요. 서명을 판정하는 것은 그 체인이기 때문입니다.

체인 식별

ZooBC의 유일한 네트워크 식별자는 제네시스 블록 해시입니다. 노드는 피어 연결 전에 이를 비교하며, 네트워크 ID 바이트는 없습니다. GET /api/v1/node/info는 v0.4.0에서 새로 추가된 두 필드와 함께 이를 반환합니다:

"genesis_hash": "090ab3c7...", "signing_version": 2, "signing_tag": "ZBC-TX"

signing_version 이 필드가 없으면 노드가 v0.4.0 이전 버전이며 바인딩되지 않은 레거시 다이제스트를 강제한다는 뜻입니다. 현재 체인만 지원하는 호스트는 이 필드가 없을 때 "여기서는 서명하지 않음"으로 처리할 수 있습니다. 노드는 소문자 hex를, 아카이브 API는 대문자 hex를 출력하므로 대소문자를 구분하지 않고 디코딩하고, 앞에 0x가 있으면 제거하세요. 어느 쪽이든 바이트는 같습니다.

어디서 읽을 것인가. 게이트웨이는 /api/v1/node/info에 노드가 아니라 아카이브 서비스로 응답합니다. 아카이브는 빌드 7ad161a4부터 자신이 미러링하는 노드의 두 서명 필드를 그대로 전달하며, 노드가 보고할 때에만 정확히 내보냅니다. signing_version는 노드 엔드포인트나 해당 빌드 이상의 게이트웨이에서 읽고, 필드가 없으면 그것을 답으로 여기지 말고 "노드에 물어보라"는 뜻으로 받아들이세요.

브로드캐스트

POST /api/v1/transactions. v0.4.0에서 변경 없음, 새 필드 없음. transaction_body_bytes는 바디만 담습니다. 노드는 이름 있는 필드들로 엔벨로프를 재구성하고, 자신의 제네시스 해시와 태그를 앞에 붙여 다이제스트를 다시 계산하므로, 모든 필드가 서명한 값과 같아야 합니다.

{
  "version": 1,
  "timestamp": 1700000000,
  "transaction_type": 1,
  "fee": 5000000,
  "sender_account_address":    "00000000 5e8eb28d ... 2152",
  "recipient_account_address": "00000000 5e8eb28d ... 2152",
  "transaction_body_bytes": "00e1f50500000000",
  "signature": "9a3b044d ... 03bb07",
  "message_hex": "...optional...",
  "escrow": { "approver_address": "...", "commission": 0, "timeout": 1700003600 }
}

성공하면 HTTP 202와 {"status":"success","duplicate":false,"transaction_hash":"..."}가 반환됩니다. duplicate:true는 노드가 이미 해당 트랜잭션을 보유하고 있다는 뜻이므로 다시 보내지 마세요. 200도 성공으로 처리하세요. 실패하면 오류 본문과 함께 400이, 부하가 높을 때는 503이 반환됩니다.

유용한 통합 테스트: 스테이징 풀은 서명을 먼저 확인하므로, "Sender account does not exist" 같은 이후 단계의 오류에 도달한 트랜잭션은 이미 서명 검증을 통과한 것입니다. 잔액이 없는 키로도 서명 경로를 검증할 수 있습니다.

서명이 거부될 때

현재로서는 체인 불일치를 다른 서명 실패와 구별할 수 없습니다. 레거시 다이제스트와 잘못된 체인의 다이제스트 모두 동일한 본문을 반환합니다:

{"success":false,"error":"Transaction validation failed: ValidationError: Invalid transaction signature","code":400}

따라서 가능성이 가장 높은 것부터 다음 순서로 원인을 점검하세요:

1태그가 누락되었거나, NUL로 끝나거나, 7바이트로 기록된 경우.
2SHA3-256 대신 Keccak-256을 사용한 경우.
3브로드캐스트 대상 체인과 일치하지 않는 제네시스 해시.
4에스크로 끝의 multi_party 바이트가 누락된 경우.
5ApprovalEscrow 바디가 36바이트가 아니라 여전히 12바이트인 경우.

개발 대상 엔드포인트

체인게이트웨이(HTTPS)서명참고
testnethttps://this-gatewayv0.4.0으로 전환 중전환이 완료되면 연동 대상
devnethttps://socialconnect.networkv0.4.0노드 11개, 포셋은 /faucet; 재시작할 때마다 제네시스 해시가 바뀜
mainnet아직 출시되지 않음v0.4.0 이상개시되면 체인에서 제네시스 해시를 읽을 것

읽기 요청은 아카이브, 게이트웨이, 노드 순으로 라우팅하세요. 브로드캐스트는 게이트웨이나 노드로 보내고, 아카이브 엔드포인트로는 절대 보내지 마세요.

제네시스 해시는 런타임에 읽고, 하드코딩하지 마세요. devnet의 해시는 재시작할 때마다 바뀝니다. 펌웨어의 해시 테이블은 화면에 이름을 표시하기 위해서만 존재하므로, 오래된 항목은 서명을 망가뜨리지 않고 “UNKNOWN CHAIN” 표시로 격하될 뿐입니다. devnet의 해시가 실제로 바뀌면 그 이전에 만든 모든 서명은 무효가 되며, 이는 기능이 의도대로 작동하고 있다는 뜻입니다.